本文现简单记录下一个签到功能的UI实现,先看效果图,如下:这是一个很简单的自定义view,原理、逻辑都比较简单,但是要注意的细节部分比较多,距离、位置等的计算要精确
接下来 我们就来实现这个功能:
首先,我们分析下这个view,有三部分构成:圆、直线、对勾,然后一个接一个绘制就行
重写view,实现几个构造方法
public class MyTestView1 extends View {
public MyTestView1(Context context) {
this(context, null);
}
public MyTestView1(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTestView1(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
在第三个构造方法里初始化画笔:
//圆节点及线的画笔
circlePaint = new Paint();
circlePaint.setColor(Color.parseColor("#fe9797"));
// circlePaint.setStrokeWidth(10);
circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
circlePaint.setAntiAlias(true);
//对勾的画笔
paint1 = new Paint();
paint1.setColor(Color.parseColor("#ffffff"));
paint1.setStrokeWidth(6);
//背景画笔
outCirclePaint=new Paint();
outCirclePaint.setColor(Color.parseColor("#eeeeee"));
// backgroundPaint.setStrokeWidth(5);
outCirclePaint.setStyle(Paint.Style.STROKE);
outCirclePaint.setAntiAlias(true);
//直线画笔
linePaint=new Paint();
linePaint.setColor(Color.parseColor("#eeeeee"));
// backgroundPaint.setStrokeWidth(5);
//文字画笔
textPaint = new Paint();
textPaint.setColor(Color.parseColor("#000000"));
textPaint.setStrokeWidth(6);
textPaint.setTextSize(40);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setTextAlign(Paint.Align.CENTER);
在onSizeChanged方法里计算节点圆半径以及设置线条宽度:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height=h;
//圆弧半径
radius =(w-20) /(count+count-1)/2;
lineWidth = radius/3;
outCircleWidth = radius/6;
outCirclePaint.setStrokeWidth(outCircleWidth);
linePaint.setStrokeWidth(lineWidth);
circlePaint.setStrokeWidth(radius/3);
outR = radius;
inR = radius-radius/6;
}
绘制灰背景+底部数字:
for(int i=0;i<count;i++){
float centerX = radius+radius*(i*4)+10;
float centerY = height/2;
canvas.drawCircle(centerX,centerY,outR,outCirclePaint);
canvas.drawText(""+(i+1),centerX,centerY+radius*3,textPaint);
if(i<count-1){//最后一个不用画线
canvas.drawLine(centerX+radius, height/2, centerX+radius+radius*2, height/2, linePaint);
}
}
效果如下:
绘制已签到的节点(浅红色节点):
canvas.drawCircle(centerX,centerY,inR,circlePaint);
drawStaticHook(canvas,centerX,centerY);
if(i==postion-2){
//签到过的部分直线也用circlePaint
canvas.drawLine(centerX+inR, height/2, centerX+radius+radius, height/2, circlePaint);
}else{
canvas.drawLine(centerX+inR, height/2, centerX+radius*4-inR, height/2, circlePaint);
}
/**
* 画对勾,无动态效果
* @param canvas
* @param centerX
* @param centerY
*/
private void drawStaticHook(Canvas canvas,float centerX,float centerY) {
//画第一根线
canvas.drawLine(centerX-radius/5-radius/3, centerY, centerX-radius/5 , centerY + radius/3, paint1);
//画第二根线
canvas.drawLine(centerX-paint1.getStrokeWidth()/2-radius/5 , centerY + radius/3, centerX + radius*2/3-radius/5, centerY -radius/3, paint1);
}
绘制签到当天节点(有动态效果)
drawDynamicCircle(canvas,centerX,centerY,circlePaint);
if(radius0>=inR){
drawHook(canvas,centerX,centerY);
if(line1_x>=radius/3 && line2_x >= radius*2/3){
circlePaint.setStrokeWidth(radius/4);
drawLastLine(canvas,centerX,centerY,circlePaint);
}
/**
* 绘制节点圆,有动态效果
* @param canvas
* @param centerX
* @param centerY
* @param paint
*/
private void drawDynamicCircle(Canvas canvas,float centerX,float centerY,Paint paint){
// Log.d("1123","radius0 : "+radius0);
// canvas.drawCircle(centerX,centerY,radius+backgroundPaint.getStrokeWidth(),backgroundPaint);
canvas.drawCircle(centerX,centerY,radius0,paint);
if(radius0<=inR){
radius0 += radius/3/step;
invalidate();
}
}
ok,这样基本功能就完成了,接下来要做的就是暴露一些属性以便动态设置如 节点总数以及当前签到的天数等等
public void setPostion(int postion) {
this.postion = postion;
invalidate();
}
public void setCount(int count){
this.count= count;
invalidate();
}
如有需要,可自行提取其他属性,这里不再一一赘述。