一晃又是周末了,时间过得真快啊。今天我们来讲一讲如何画一条线。有一些激动的小伙伴就要开喷了,画线这么简单需要你来逼逼啊。

兄弟,话不能这么说。

我们先看一下最终需要实现的效果,直接上图。


android vector 虚线 vectornator怎么画虚线_android vector 虚线


看到中间的那一条虚线么?看上去还是蛮好看的。今天我们的任务就是实现它。


画线的需求大家经常遇到,非常的简单。

<view
    android:layout_width="1px"
    android:layout_height="match_parent"
    android:background="@color/colorBlue"/>


这样就能画出一条线了。

如果是画虚线呢?就需要在drawable文件夹下先定义一个shape样式,然后在layout文件中引用它。同样也非常的简单。

我们先看一下shape的写法。


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line" >
    <stroke android:width="2dp" android:dashGap="5dp"
        android:dashWidth="4dp" android:color="@color/colorPrimary"  />
</shape>


这里有几个点需要注意一下。

第一、首先stroke 的 width 是指的描边的宽度,这里一定要注意:在引用这个shape的时候,我们包裹这个虚线的布局宽高一定要大于这个描边的width,不然虚线显示不出来。

其实也比较好理解,你样式里面定义的描边宽度比外面容器的宽度还要大,当然不能显示啊。


举个例子,像现在我这里stroke 的 width 是2dp。如果我要画一条水平的虚线,在引用的时候我线条的 height 必须要不小于 2dp 才能显示。


第二、我们要关闭硬件加速,不然明明在Android Studio上预览是虚线,一运行到真机上就会变成了实线。


因为安卓3.0之后开启了硬件加速,而dashGap不支持硬件加速,所以关了就能正常显示虚线了。


关闭的方式也有几种,我们来分享一下。

1.直接在Manifest.xml中,application标签下加入这样一句话:android:hardwareAccelerated = "false" 。

2.在代码中获取我们的虚线view,view.setLayerType(View.LAYER_TYPE_SOFTWARE,null);

3.直接在xml中view的标签下加一句:android:layerType = "software";


当然,你知道我的尿性,我一般推荐的方法放在最后面。

第一种让整个应用都关闭硬件加速了;第二种还要在activity里面去绑定这个view,就为了加这么一句话,这也太麻烦了吧。所以我个人推荐第三种。当然怎么选看你自己。


说了这么多,早就有小伙伴按耐不住了。

“你这是你文章一上来说要实现的效果么,大骗子。”

不要着急嘛,好文章和好酒是一样一样的,总是慢慢品才有味道的。


我前面说的是如何实现虚线,接下来就是实现我们文章一开始给出那个样式的虚线了。

看到效果的第一眼,我们应该就知道,这要用自定义view是没得跑了。


当然有小伙伴们要说,你说自定义view没得跑,那我就不用自定义view,我就要用LinearLayout里面包裹view来实现。

对于这种小伙伴,我只能说:“好好好,你棒棒。我服你。”


不多逼逼,直接上代码再分析。


public class GorgeousDottedLine extends View{
    private int width;
    private int height;
    private int[] colors =new int[]{0xFFE91313,0xFFE98713,0xFF1378E9,0xFFE91382};
    public GorgeousDottedLine(Context context) {
        super(context);
    }

    public GorgeousDottedLine(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if(specMode == MeasureSpec.EXACTLY){
            width = specSize;
        }else {
            width =getDefaultWidth();
            if(specMode == MeasureSpec.AT_MOST){
                width = Math.min(width,specSize);
            }
        }
        return width;
    }

    private int getDefaultWidth() {
        return LinearLayout.LayoutParams.MATCH_PARENT;
    }


    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if(specMode==MeasureSpec.EXACTLY){
            height = specSize;
        }else {
            height= getDefaultHeight();
            if(specMode == MeasureSpec.AT_MOST){
                height = Math.min(height,specSize);
            }
        }
        return height;
    }

    private int getDefaultHeight() {
        return DisplayUtils.dp2px(getContext(),4);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint  = new Paint();
        paint.setStrokeWidth(height);
        paint.setAntiAlias(true);
        paint.setStrokeCap(Paint.Cap.ROUND);
        int count = 10;
        int total = count*4+count-1;
        for(int i=0;i<count;i++){
            paint.setColor(colors[i%4]);
            canvas.drawLine(width*(i*4+i)/total,height/2,width*(5*i+4)/total,height/2,paint);
        }
    }
}




整体的代码思路也比较简单,首先定义宽高和色号,测量都没什么好说的。

我们重点讲一下onDraw()方法。

我用画线而不是画矩形的的方式实现,所以我设置了一下strokeWidth 等于 height,同时在画的时候drawLine的 y 值我设置的是height/2。


count是这条虚线总共有几个小段,下面的计算是把每一条虚线小段的宽度定义为每一条虚线小段的间隙宽度的4倍,所以虚线小段数有count个,虚线小段间隙有count-1个。

然后计算每一个点的位置,由于他们是固定的规律,我就通过循环将他们画出来。


思路就是这样。拜了个拜。