自定义View一直是一个很头疼的问题,之前想写了很久就是写不出来,今天来写点入门的东西。
首先我们为什么会觉得自定义view很难呢?
- 第一是我们不知道怎么绘制,画线?画文字?
- 第二各种距离的测量计算
- 第三个是事件的处理,点击?滑动?
先来看看我们的需求!
!这是要实现的效果1
最终我做的效果
大概分析一下就开干,然后边做边思考解决出现的问题!看着很简单,上面有一个进度条,但是不是均匀分布,中间还有两个圆圈可以点击,用来更改状态。最后还有个斜着的的已放弃!
1)我们一般会考虑XML的属性,我们需要什么参数?这个还是边做边加吧!
这个仅仅是测试的,需要什么加什么,获取XML 的值实在构造里面处理的,如上!
2)进入测量阶段!
我们在测量的时候最担心的是不同的mode带来的问题,比如说我设置了wrap_content
的高度,那我们是不是应该给一个默认的值呢,如果设置的是精准的高度那么我们就可以直接赋值了!
当然我们还应该考虑padding值,这里我们先不管,先把功能实现,后续的一些细节问题,如果前期懂了那些都不是啥问题!
当然我们看到的这个代码是我写的第二次,为嘛呢,因为我们还有一个适配问题没有考虑,本人比较懒,这个view的模式基本是死的,也就是说两个圆圈的位置是死的,所以我就用了百分比布局方式,这样就可以解决适配问题了!
3)
开始画画啦!
首先我们要划线,那么从什么位置开始画画到什么位置,第一条线当然是从0到第一个圆圈位置,你看到我定义的是point_1,那么我们就开始画第一条线
linePaint.setColor(COLOR_SELECT); canvas.drawLine(50, BASIC_HEIGHT, point_1, BASIC_HEIGHT, linePaint);
Paint用来设置我们需要的颜色、线的粗细,等等属性,,canvas负责绘画
BASIC_HEIGHT是我定义的一个基本的高度,这样第一天条线就画完了
我们开始绘制第一个圆,当然这里我画的其实是一个Icon,这个是阿里云的把图片转文字的一种方式,所以 我画上去的是文字
rectF = new Rect(); textPaint.getTextBounds(UNSELECT_ICON, 0, UNSELECT_ICON.length(), rectF); canvas.drawText(UNSELECT_ICON, point_1 - 3, BASIC_HEIGHT + rectF.height() / 2, textPaint);
我们现用 RectF来测量文字的宽高,这样rectf就有了文字宽高的值,便于我们是用。
为了让这个圆圈正好在中间,我们是不是要知道圆的高,让后把高度提高一半才正好落 在中轴线上,所以看我这里就是这么做的!
后面再画圆画直线就一样了,
绘制文字,绘制文字的时候我们仅需要知道在什么位置绘制,也是根据我设置的那几个点来的,文字关于点对称,那么要求文字的起始点比我设置的点要小文字长度的一半
textPaint.getTextBounds(proStrings[1], 0, proStrings[1].length(), rectF5);
canvas.drawText(proStrings[1], point_1 + rectF.width() / 2 - rectF5.width() / 2,
BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint);我们这里加了一个文字的高度?因为文字的坐标实在坐下角的,所以罗。。
point_1 + rectF.width() / 2 - rectF5.width() / 2 这个宽度还要包括圆的一半对吧,位 置其实就这么算出来的!private void drawPro(Canvas canvas, int progress) { linePaint.setColor(COLOR_UNSELECT); linePaint.setStrokeWidth(LINE_HEIGHT); linePaint.setAntiAlias(true); textPaint.setColor(COLOR_UNSELECT); textPaint.setStrokeWidth(3); textPaint.setTextSize(textSize); textPaint.setTypeface(iconfont); textPaint.setAntiAlias(true); paddingLeft = getPaddingLeft(); paddingTop = getPaddingTop(); paddingRight = getPaddingRight(); paddingBottom = getPaddingBottom(); width = getWidth() - paddingLeft - paddingRight; height = getHeight() - paddingTop - paddingBottom; BASIC_HEIGHT = BASIC_HEIGHT + paddingTop; if (progress == 0) { linePaint.setColor(COLOR_SELECT); canvas.drawLine(50, BASIC_HEIGHT, point_1, BASIC_HEIGHT, linePaint); rectF = new Rect(); textPaint.getTextBounds(UNSELECT_ICON, 0, UNSELECT_ICON.length(), rectF); canvas.drawText(UNSELECT_ICON, point_1 - 3, BASIC_HEIGHT + rectF.height() / 2, textPaint); linePaint.setColor(COLOR_UNSELECT); canvas.drawLine(point_1 - 3 + rectF.width(), BASIC_HEIGHT, point_2, BASIC_HEIGHT, linePaint); canvas.drawText(UNSELECT_ICON, point_2 - 3, BASIC_HEIGHT + rectF.height() / 2, textPaint); canvas.drawLine(point_2 - 3 + rectF.width(), BASIC_HEIGHT, screeenWidth - 50, BASIC_HEIGHT, linePaint); } else if (progress == 1) { linePaint.setColor(COLOR_SELECT); canvas.drawLine(50, BASIC_HEIGHT, point_2, BASIC_HEIGHT, linePaint); rectF = new Rect(); textPaint.getTextBounds(UNSELECT_ICON, 0, UNSELECT_ICON.length(), rectF); canvas.drawText(UNSELECT_ICON, point_2 - 3, BASIC_HEIGHT + rectF.height() / 2, textPaint); linePaint.setColor(COLOR_UNSELECT); canvas.drawLine(point_2 - 3 + rectF.width(), BASIC_HEIGHT, screeenWidth - 50, BASIC_HEIGHT, linePaint); } else if (progress == 2) { linePaint.setColor(COLOR_SELECT); canvas.drawLine(50, BASIC_HEIGHT, point_2, BASIC_HEIGHT, linePaint); rectF = new Rect(); textPaint.setColor(COLOR_SELECT); textPaint.getTextBounds(SELECT_ICON, 0, SELECT_ICON.length(), rectF); canvas.drawText(SELECT_ICON, point_2 - 3, BASIC_HEIGHT + rectF.height() / 2, textPaint); linePaint.setColor(COLOR_UNSELECT); canvas.drawLine(point_2 - 1 + rectF.width(), BASIC_HEIGHT, screeenWidth - 50, BASIC_HEIGHT, linePaint); } else if (progress == 3) { linePaint.setColor(COLOR_SELECT); rectF = new Rect(); textPaint.setColor(COLOR_SELECT); textPaint.getTextBounds(SELECT_ICON, 0, SELECT_ICON.length(), rectF); canvas.drawText(SELECT_ICON, point_3 - rectF.width() / 2 - 2, BASIC_HEIGHT + rectF.height() / 2, textPaint); canvas.drawLine(50, BASIC_HEIGHT, point_3 - rectF.width() / 2 - 2, BASIC_HEIGHT, linePaint); linePaint.setColor(COLOR_UNSELECT); canvas.drawLine(point_3 + rectF.width() / 2, BASIC_HEIGHT, screeenWidth - 50, BASIC_HEIGHT, linePaint); } else if (progress == 4) { linePaint.setColor(COLOR_SELECT); rectF = new Rect(); textPaint.setColor(COLOR_SELECT); textPaint.getTextBounds(SELECT_ICON, 0, SELECT_ICON.length(), rectF); canvas.drawText(SELECT_ICON, screeenWidth - rectF.width(), BASIC_HEIGHT + rectF.height() / 2, textPaint); canvas.drawLine(50, BASIC_HEIGHT, screeenWidth - rectF.width(), BASIC_HEIGHT, linePaint); } textPaint.setColor(mContext.getResources().getColor(R.color.color_editText)); textPaint.setTextSize(40); textPaint.setAntiAlias(true); Rect rectF5 = new Rect(); textPaint.getTextBounds(proStrings[0], 0, proStrings[0].length(), rectF5); canvas.drawText(proStrings[0], 50, BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint); textPaint.getTextBounds(proStrings[1], 0, proStrings[1].length(), rectF5); canvas.drawText(proStrings[1], point_1 + rectF.width() / 2 - rectF5.width() / 2, BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint); textPaint.getTextBounds(proStrings[2], 0, proStrings[2].length(), rectF5); canvas.drawText(proStrings[2], point_2 + rectF.width() / 2 - rectF5.width() / 2, BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint); textPaint.getTextBounds(proStrings[3], 0, proStrings[3].length(), rectF5); canvas.drawText(proStrings[3], point_3 - rectF5.width() / 2, BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint); textPaint.getTextBounds(proStrings[4], 0, proStrings[4].length(), rectF5); canvas.drawText(proStrings[4], screeenWidth - 60 - rectF5.width(), BASIC_HEIGHT + rectF5.height() + TOP_HEIGHT, textPaint); if (isGiveUp) { canvas.rotate(-10, point_3, BASIC_HEIGHT); Rect rect = new Rect(); textPaint.getTextBounds(GIVE_UP_STR, 0, GIVE_UP_STR.length(), rect); textPaint.setColor(mContext.getResources().getColor(R.color.color_textView)); canvas.drawText(GIVE_UP_STR, point_3 - rect.width() - 20, BASIC_HEIGHT + rect.height() / 2, textPaint); rect.right = (int) point_3; rect.left = (int) (point_3 - 150); rect.top = BASIC_HEIGHT - 30; rect.bottom = BASIC_HEIGHT + 30; RectF rectf = new RectF(rect.left, rect.top, rect.right, rect.bottom); linePaint.setStyle(Paint.Style.STROKE); canvas.drawRoundRect(rectf, 10, 10, linePaint); }
}
这里我用了了一个progress进度来控制绘画哪一步的样式
5.
那我们如何画放弃呢?一个圆角矩形和文字,同时旋转一定的角度!其实就是对画布 进行旋转,如果我们画完这个还需要画其他的需要canvas的save方法保存状态,不然后 面画的就都是斜着的了。
if (isGiveUp) {
canvas.rotate(-10, point_3, BASIC_HEIGHT);
Rect rect = new Rect();
textPaint.getTextBounds(GIVE_UP_STR, 0, GIVE_UP_STR.length(), rect);
textPaint.setColor(mContext.getResources().getColor(R.color.color_textView));
canvas.drawText(GIVE_UP_STR, point_3 - rect.width() - 20, BASIC_HEIGHT + rect.height() / 2, textPaint);
rect.right = (int) point_3;
rect.left = (int) (point_3 - 150);
rect.top = BASIC_HEIGHT - 30;
rect.bottom = BASIC_HEIGHT + 30;
RectF rectf = new RectF(rect.left, rect.top, rect.right, rect.bottom);
linePaint.setStyle(Paint.Style.STROKE);
canvas.drawRoundRect(rectf, 10, 10, linePaint);
}
6.现在貌似一个毛坯已经出来了,还很毛,没有任何的细节处理!
先来点击事件吧。
我们所有的点击处理都早ontouchEvent里面处理的,我点击圆,我怎么知道点击在圆上? 我们想这个是一个整体的view没办法区分我是不是点在圆上,那么我们能不能根据圆的坐标点呢,稍微的放大!!
case MotionEvent.ACTION_UP:
EndTime = System.currentTimeMillis();
float dy = event.getY() - y;
float dx = event.getX() - x;
if (EndTime - StartTime < 200) {
if (Math.abs(dx) < 3 && Math.abs(dy) < 3) {
if(!isGiveUp){
if (mOnClickListener != null) {
if (event.getX() > point_1 - 50 && event.getX() < point_1 + 50) {
mOnClickListener.onClick(1);
}
if (event.getX() > point_2 - 50 && event.getX() < point_2 + 50) {
mOnClickListener.onClick(2);
}
}
}
}
}
break;
你看点击事件有了,这里用的就是一个简单的回调,当然我没有限制y的范围,这个根据自己愿意吧!
7.最后我们还有一个功能侧滑!
我们要侧滑,也就是把自己向右移动某个距离,怎么移动。我这里就懒一点,用一个动画搞定吧,你可以根据滑动的距离来决定view的滑动多少!
case MotionEvent.ACTION_MOVE:
if (Math.abs(event.getY() - y) < 10 && event.getX() - x < -20) {
if (!isOpen && !isGiveUp&&mProgress<3) {
open();
}
} else {
if (Math.abs(event.getY() - y) < 10 && event.getX() - x > 30&&isOpen) {
close();
}
}
public void open() {
ObjectAnimator animator = ObjectAnimator.ofFloat(this, "translationX", 0, -CommonUtils.dip2px(mContext, 60));
animator.setDuration(200);
animator.start();
isOpen = true;
}
public void close() {
ObjectAnimator animator = ObjectAnimator.ofFloat(this, "translationX", -CommonUtils.dip2px(mContext, 60), 0);
animator.setDuration(200);
animator.start();
isOpen = false;
}
这个时候再回去看开始的东西
我们是不是需要一个进度、还有能否侧滑,是不是放弃状态的、字体大小,颜色,线的颜色,高度等等值都可以做成属性值!