Android 自定义-充电View(一)
之前搞完了基础的自定义中的 canvas,paint,那么就来试一下吧。
好了来试一下这个View 是怎么实现的,今天先不搞动画之类的,只是做对前知识的复习,这个图分成两块,上面是三个圆弧加一个圆,然后内部有个Text,下面有个曲线,然后一直画到下面的直线,那就是三个方法canvas.drawCircle(),canvas.drawArc(),canvas.drawText(),canvas.drawLine();
首先画圆弧7(其实这里并不是7点方向不是很严谨,应该是120°,只说个7点大概位置大伙知道就好)点方向顺时针到5点方向,canvas.drawArc(rectF,120,300,false,outPaint),通过改变rectF的大小来实现三个外圈的实现,再通过paint.setAlpha(0-255)来实现逐渐透明。最后画个圆,中间写上Text,写文字涉及到基线的问题,这里贴上代码:
Rect rect = new Rect(getWidth()/2-150,getHeight()/2-150,getWidth()/2+150,getHeight()/2+150);//画一个矩形
Paint textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(100);
textPaint.setStyle(Paint.Style.FILL);
//该方法即为设置基线上那个点究竟是left,center,还是right 这里我设置为center
textPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;
float bottom = fontMetrics.bottom;bottom
int baseLineY = (int) (rect.centerY() - top/2 - bottom/2);
canvas.drawText("90%",rect.centerX(),baseLineY,textPaint);
然后画出弧线,再接上一个直线,弧线这里先用贝塞尔的思想,直接写上代码实现弧线的操作,关于贝塞尔等以后写博客的时候再介绍。
Path path = new Path();
path.moveTo(x,y);//这里的x,y为出发点的坐标。
path.quadTo(x1,y1,x2,y2);//这里的x1,y1 就是参照点,x2,y2就是终点。先mark。
最后通过drawLine来实现几个竖直的线,至于动画这里先不研究。好了看上去思路在这了,我们来看看实际的效果,首先这个充电的View有个颜色的渐变,如何实现这个颜色的渐变,之前只知道Paint.setColor这个方法而且设置的也是一个颜色。下面有两个思路:
-
画圆的时候一个弧一个弧的画,通过不停的改变画笔的颜色来实现,很明显这个太坑了,根本做不动,毕竟人家的颜色还是渐变的。
-
能否找到像shape一类的操作来实现颜色的渐变:百度一下一看,哎嘿有个Shader类,其中这里使用到的是SweepGradient,这个东西有一些坑,这个类内容很少,可以通过注释了解这个函数的大概,有两种方法(1)
SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1)这个方法其实就是坐标中心x,y,然后一个startColor,一个endColor。实现渐变。
(2)SweepGradient(float cx, float cy,
@NonNull @ColorInt int colors[], @Nullable float positions[])这个方法就有意思了,colors[]表示是要填充的颜色,position表示每个颜色占的比重,为0-1。为null时候,系统自动给颜色进行均匀分布,比如colors里面有三个元素,为null他自己会均匀的分布,你也可以设置position这个就可以实现每个元素占的比重,比如float{0,0.5,1}那么这三个颜色就是从0(也就是开始的地方),然后到一半(0.5)实现的是第一个颜色到第二个颜色的渐变,(0.5-1)实现的是第二个颜色到第三个颜色的渐变。如下代码:float[] ff1 = new float[3]; ff1[0] = 0; ff1[1] = (float) (0.5); ff1[2] = (float)(1); colors = new int[]{0xFFE61AE6,Color.BLUE,Color.RED}; sweepGradient0 = new SweepGradient(getWidth()/2, getHeight()/2,colors,ff1); innerPaint.setShader(sweepGradient0); canvas.drawCircle(getWidth()/2,getHeight()/2,150,innerPaint);
注意这里画圆的时候所有的画笔开始的地方都是从3点方向开始的,这样就能看出{0,0.5,1}的区分了,具体可以再尝试做实验。
好了问题又来了我们可以直接画弧形从7点开始画,但是用SweepGradient的时候发现这玩意并没有跟着画笔走,而是还是从三点方向开始往下,那这样跟我们的预期肯定不符合啊,我们得让他跟着画笔的开始走,这时候就要用到一个新的类Matrix这里看一下使用方法:
Matrix matrix = new Matrix();
matrix.setRotate(120,getWidth()/2,getHeight()/2);
sweepGradient0.setLocalMatrix(matrix);
这样就可以实现颜色也从7点方法开始画了。附上完整代码:
public class ChargeView extends View {
Paint outPaint,innerPaint,textPaint;
int startColor;
int midColor;
int endColor;
private int[] colors;
String TAG = "ChargeView";
float PADDING = 20;
public ChargeView(Context context) {
this(context,null);
}
public ChargeView(Context context,AttributeSet attrs) {
this(context, attrs,0);
}
public ChargeView(Context context,AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ChargeView);
startColor = typedArray.getColor(R.styleable.ChargeView_start_color,Color.RED);
midColor = typedArray.getColor(R.styleable.ChargeView_middle_color,Color.GREEN);
endColor = typedArray.getColor(R.styleable.ChargeView_end_color,Color.BLUE);
typedArray.recycle();
initView();
}
private void initView(){
outPaint = new Paint();
innerPaint = new Paint();
textPaint = new Paint();
outPaint.setStyle(Paint.Style.STROKE);
innerPaint.setStyle(Paint.Style.STROKE);
outPaint.setAntiAlias(true);
innerPaint.setAntiAlias(true);
textPaint.setAntiAlias(true);
outPaint.setStrokeWidth(2);
innerPaint.setStrokeWidth(30);
textPaint.setTextSize(15);
SweepGradient sweepGradient;
colors = new int[]{Color.BLUE,startColor,Color.BLUE};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.getSize(heightMeasureSpec));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
SweepGradient sweepGradient0;
Matrix matrix = new Matrix();
matrix.setRotate(120,getWidth()/2,getHeight()/2);
Log.e(TAG, "onDraw: " );
float[] ff = new float[3];
ff[0] = 0;
ff[1] = (float) (5/12.0);
Log.e("TAG",(float) (5/12.0)+"");
ff[2] = (float)(5/6.0);
sweepGradient0 = new SweepGradient(getWidth()/2, getHeight()/2,colors,ff);
sweepGradient0.setLocalMatrix(matrix);
outPaint.setShader(sweepGradient0);
drawCircle(canvas,200);
outPaint.setAlpha(150);
drawCircle(canvas,190);
outPaint.setAlpha(50);
drawCircle(canvas,180);
colors = new int[]{0xFFE61AE6,Color.BLUE,0xFFE61AE6};
sweepGradient0 = new SweepGradient(getWidth()/2, getHeight()/2,colors,null);
sweepGradient0.setLocalMatrix(matrix);
innerPaint.setShader(sweepGradient0);
canvas.drawCircle(getWidth()/2,getHeight()/2,150,innerPaint);
textPaint.setColor(Color.BLACK);
outPaint.setColor(0xFF1196EE);
canvas.drawLine(getWidth()/2-25,getHeight()/2+165,getWidth()/2-25,getHeight(),outPaint);
canvas.drawLine(getWidth()/2,getHeight()/2+165,getWidth()/2,getHeight(),outPaint);
canvas.drawLine(getWidth()/2+25,getHeight()/2+165,getWidth()/2+25,getHeight(),outPaint);
Rect rect = new Rect(getWidth()/2-150,getHeight()/2-150,getWidth()/2+150,getHeight()/2+150);//画一个矩形
Paint textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(100);
textPaint.setStyle(Paint.Style.FILL);
//该方法即为设置基线上那个点究竟是left,center,还是right 这里我设置为center
textPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom
int baseLineY = (int) (rect.centerY() - top/2 - bottom/2);//基线中间点的y轴计算公式
canvas.drawText("90%",rect.centerX(),baseLineY,textPaint);
}
private void drawCircle(Canvas canvas,int radius){
float x = getWidth()/2;
float y = getHeight()/2;
RectF rectF = new RectF(x-radius,y-radius,x+radius,y+radius);
canvas.drawArc(rectF,120,300,false,outPaint);
Path path = new Path();
path.moveTo(x-radius/2,(float)(y+radius*Math.cos(Math.PI/6)));
path.quadTo(x-radius/4,(float)(y+radius*Math.cos(Math.PI/6))+20,x-radius/4,(float)(y+radius*Math.cos(Math.PI/6)+100));
canvas.drawPath(path,outPaint);
canvas.drawLine(x-radius/4,(float)(y+radius*Math.cos(Math.PI/6)+100),x-radius/4,y*2,outPaint);
path = new Path();
path.moveTo(x+radius/2,(float)(y+radius*Math.cos(Math.PI/6)));
path.quadTo(x+radius/4,(float)(y+radius*Math.cos(Math.PI/6))+20,x+radius/4,(float)(y+radius*Math.cos(Math.PI/6)+100));
canvas.drawPath(path,outPaint);
canvas.drawLine(x+radius/4,(float)(y+radius*Math.cos(Math.PI/6)+100),x+radius/4,y*2,outPaint);
}
}
最后上效果图,虽然有很大的差距但是细节扣一下还是能看的。
ChargeView.png