前言:UI需要如下效果,开始想用.9图实现,但是UI说.9图可能会错位,emmmm。。。好吧,还是自己用代码写一个吧,反正代码也不多。
一、需求分析
这个效果可以理解为两个半圆+间断的线组成。
1、定义这个控件需要如下属性:
(1)两端半圆直径(2)两端半圆颜色
(3)分割线颜色
(4)分割线长度(未定义,需要的可以自己加一下)
(5)分割线间隔长度 (未定义,需要的可以自己加一下)
2、自定义属性
<declare-styleable name="MyDividLine"> <attr name="divid_line_color" format="color" /> <attr name="port_shape_color" format="color" /> <attr name="port_shape_height" format="dimension" /> </declare-styleable>
3、定义属性变量
//分割线颜色 private int mDividLineColor; //两端半圆颜色 private int mPortShapeColor; //两端半圆高度(直径) private int mPortShapeHeight; //两端半圆半径 private int mPortShapeRadius;
4、为上面的属性定义默认值
//分割线默认颜色 private final int DIVIDLINE_DEFAULT_COLOR = 0xFFD9D9D9; //两端半圆默认颜色 private final int PORTSHAPE_DEFAULT_COLOR = 0xFFF2F2F2; //两端半圆默认高度(直径) private final int PORTSHAPE_DEFAULT_HEIGHT = dp2px(15);
二、编码
一个自定义view的编码步骤可以大体分为:获取自定义属性 —— onMeasure() —— onDraw()。
1、获取自定义属性
在构造方法中我们获取想要的自定义属性,初始两端圆半径的值、画笔。
public MyDividLine(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyDividLine);
mDividLineColor = ta.getColor(R.styleable.MyDividLine_divid_line_color, DIVIDLINE_DEFAULT_COLOR);
mPortShapeColor = ta.getColor(R.styleable.MyDividLine_port_shape_color, PORTSHAPE_DEFAULT_COLOR);
mPortShapeHeight = (int) ta.getDimension(R.styleable.MyDividLine_port_shape_height, PORTSHAPE_DEFAULT_HEIGHT);
mPortShapeRadius = mPortShapeHeight / 2;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(dp2px(1));
ta.recycle();
}
2、重写onMeasure():
在测量高度时,不要忘记判断 MeasureSpec.UNSPECIFIED这个模式,否则在scrollview中会显示不出来。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthVal = MeasureSpec.getSize(widthMeasureSpec); int heightVal = measureHeight(heightMeasureSpec); setMeasuredDimension(widthVal, heightVal); }
/** * 测量控件高度 * * @param heightMeasureSpec * @return */ private int measureHeight(int heightMeasureSpec) { int height = MeasureSpec.getSize(heightMeasureSpec); int mode = MeasureSpec.getMode(heightMeasureSpec); int result = 0; if (mode == MeasureSpec.EXACTLY) { result = height; } else if (mode == MeasureSpec.AT_MOST) { result = getPaddingTop() + getPaddingBottom() + mPortShapeHeight; }else if(mode == MeasureSpec.UNSPECIFIED){ result = getPaddingTop() + getPaddingBottom() + mPortShapeHeight; } return result; }
3、重写onDraw():
这个环节需要对canvas、paint、path具有一定的基础,如果基础比较薄弱的同学可以参考启舰大神的博客Android自定义控件三部曲中的绘图篇
onDraw()里我们画的顺序是:左端半圆 —— 分割线 —— 右端半圆。
首先设置画笔参数:
canvas.save(); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mPortShapeColor);
(1)画左端半圆
然后构建一个矩形,通过这个矩形画出半圆RectF rectF = new RectF(0, 0, mPortShapeHeight, mPortShapeHeight);再然后我们就可以画左端的半圆了,不过在画之前我们需要将画布左移一个半径的距离。为什么要先移动一个半径的距离呢?我用一张图表示。
因为我们画半圆是通过canvas.drawArc()这个方法,这个方法通过我们在上面定义的矩形内画一段圆弧(半圆也可以看成圆弧),虽然只画了一半圆,但是另一半的位置还是会留着。因为它是为我们预留了一个矩形的大小来让我们画圆弧。如果画布不移动的话,出来的效果就是下面的样子。
好了,搞清楚后就可以画半圆了
canvas.translate(-mPortShapeRadius, 0); canvas.drawArc(rectF, 270, 180, true, mPaint);//从270度开始画,画180度圆弧。 canvas.restore();
这里canvas.restore()是将画布的位置复原。如果对canvas状态保存复原不懂的接着去上面启舰大神的博客里找介绍canvas图层的博文。
(2)画虚线
半圆搞定了,emmmm,,,虚线呢??小事小事,画虚线我们使用path来搞定,在drawPath前,我们先给paint设置一个
DashPathEffect。什么鬼,这是什么东西,莫慌莫慌,光看字面意思可以翻译为短跑路径效果,跑一下..停..跑一下..停.....是不是
恍然大悟,这不就是我们想要的虚线效果吗。
mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(mDividLineColor); DashPathEffect effect = new DashPathEffect(new float[]{5, 5}, 0); mPaint.setPathEffect(effect); Path path = new Path(); path.moveTo(mPortShapeRadius + 3,getMeasuredHeight() / 2); path.lineTo(getMeasuredWidth() - mPortShapeRadius - 3,getMeasuredHeight() / 2); //虚线两端偏移3个像素 canvas.drawPath(path,mPaint);
(3)画右端半圆
到这里我们可以说是胜券在握了,你可以泡一杯茶放松放松了。。画右端就是把画左端反过来。注意不要忘了先右移画布哦~
mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mPortShapeColor); rectF = new RectF(getMeasuredWidth() - mPortShapeHeight, 0, getMeasuredWidth(), mPortShapeHeight); canvas.translate(mPortShapeRadius, 0); canvas.drawArc(rectF, 90, 180, true, mPaint);//从90度开始画,画180度圆弧。
最终效果图:
总结:
一个简单的仿电影票分割线控件就完成了,没什么大问题,如果对自定义View有什么不懂的,接着去找万能的启舰大神。。把他的自定义View三部曲仔细的过一遍,再自定义view的时候,说完全没问题吧有点太狂了,中等没问题吧。好了,我去喝茶了。。