Android自定义View--ClockView

Android自定义View--ClockView

前一篇博客中,简要介绍了关于自定义View的流程,以及一些重要的函数,接下来我会编写一个自己的CliokView(继承自View类)。

实现效果图如下:


在这里插入图片描述

第一步编写构造函数

​ 通常使用的构造函数有三个,分别如下

public ClockView(Context context){
    this(context,null);
}
public ClockView(Context context, AttributeSet attrs){
    super(context,attrs);
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

第二步编写onMeasure函数

​ onMeasure有两个参数,分别是宽高与对应方向上的测量模式合成的一个值,不能直接使用此参数作为宽高。我们首先使用getMode方法获取测量模式,如果测量模式为AT_MOST(表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小),那么我们手动设置View的宽高为MATCH_PARENT。其余情况不做修改。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int modeWidth  = MeasureSpec.getMode(widthMeasureSpec);
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

    //没有指定宽高,使用了wrap_content,则手动指定宽高为MATCH_PARENT
    if (modeWidth == AT_MOST && modeHeight == AT_MOST){
        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
        setLayoutParams(layoutParams);
    }
}

第三步编写onSizeChanged方法

​ onSizeChanged方法在View的尺寸发生变化时调用,方法有四个参数,分别代表变化之前的宽高与变化之后的宽高。我们定义两个内部变量mWidth与mHeight,并将比那花后的宽高赋给他们,方便使用。

​ 之后定义三个变量CenterX,CenterY,mClockLength,分别代表Clock的圆心坐标以及半径。

protected void onSizeChanged(int w,int h,int oldw,int oldh){
    super.onSizeChanged(w,h,oldw,oldh);
    mWidth = w;
    mHeight = h;
    CenterX = mWidth / 2f;
    CenterY = mHeight / 2f;
    mClockLength = Math.min(mWidth,mHeight) / 2.4f;
}

第四步onDraw方法

​ 由于我们继承的时View类,定义的View也就没有子View。因此也就没有onLayout方法。

​ 在Android绘图时我们要用到Canvas(画布),Paint(画笔)。

​ 首先定义一个init函数,用来初始化画笔,并在构造函数中调用它。

private void init(){
    mClockPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
    mClockPaint.setStyle(Paint.Style.FILL);//填充
    mClockPaint.setColor(Color.parseColor("#ededec"));//设置颜色

    mScalePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mScalePaint.setStyle(Paint.Style.STROKE);
    mScalePaint.setStrokeCap(Paint.Cap.ROUND);#线条末端圆角,也即线条长度=线的长度+线的宽度
    mScalePaint.setColor(Color.BLACK);

    mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPointPaint.setStyle(Paint.Style.FILL);
    mPointPaint.setColor(Color.BLACK);

    mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mLinePaint.setStyle(Paint.Style.STROKE);
    mLinePaint.setStrokeCap(Paint.Cap.ROUND);
    mLinePaint.setColor(Color.BLACK);
    mLinePaint.setStrokeWidth(5);

    mSecondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mSecondPaint.setStyle(Paint.Style.STROKE);
    mSecondPaint.setColor(Color.RED);
    mSecondPaint.setStrokeWidth(5);//笔刷宽度

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(Color.RED);
}

​ 下一步定义drawClock函数,此函数真正负责绘制图形。

private void drawclock(Canvas canvas){

    canvas.drawCircle(CenterX,CenterY,mClockLength,mClockPaint);//画出整个表盘
    //将坐标中心移动到时钟中心
    canvas.translate(CenterX, CenterY);
    canvas.save();//保存画布状态,压栈
    for (int i=0; i<=360; i+=30) {//画出clock的刻度,并旋转30度,最终转一圈画出12个刻度
        canvas.drawLine(0,mClockLength * 0.82f * -1,0,mClockLength * 0.9f * -1, mScalePaint);
        canvas.rotate(30);
    }
    canvas.drawCircle(0,0,20,mPointPaint);//clock的中心
    canvas.restore();//取出初始的画布状态
    drawHour(canvas);
    drawMinute(canvas);
    drawSecond(canvas);
}

​ android手机屏幕的初始坐标系以屏幕左上角为原点,向右向下分别为x方向与y方向的正方向。canvas.translate方法有两个参数,此方法将坐标原点移动到所给定的坐标。上边代码将坐标原点移动到clock中点,方便之后操作。关于刻度的绘制,我是用的方法是旋转,先绘制出表盘上方12点钟方向的刻度,之后旋转画布,绘制出其他的刻度。当然如果不想这样旋转画布,也可以使用Math.sin和Math.cos方法计算出刻度的坐标,然后绘制。

但是画布操作是不可逆的,也就是说前边的画布操作会影响到后边的操作,因此我们需要对画布装台进行保存和回滚。而save方法就是将当前画布的状态保存,然后放入一个栈中。restore方法从栈顶将画布状态取出,并恢复。

​ 绘制完刻度之后就需要绘制指针

private void drawHour(Canvas canvas){

    double i = mHour % 12d * 30d + mMinute / 2d;
    float y = (float)(Math.cos(Math.PI * i / 180) * mClockLength * -0.5f);
    float x = (float)(Math.sin(Math.PI * i / 180) * mClockLength * 0.5f);

    canvas.drawLine(0,0,x,y,mLinePaint);
}

​ mHour,mMinute是定义来保存时间值的变量。

第五步提供外部接口

​ 作为一个自定义的clock,可以设置自动获取时间,也可以在外部设置时间。在这里我定义了一个外部设置时间的方法setTime,当然在设置时间之前也需要检查时间格式是否标准。

public void setTime(int hour, int minute, int second){
    this.mHour = hour;
    this.mMinute = minute;
    this.mSecond = second;

    checkTime();

    postInvalidate();//刷新UI,关于postInvalidate与Invalidate区别,自行百度
}

至此,一个自定义的ClockView完成了,可以在布局文件中调用。当然这只是第一步,简单的绘制出View,接下来还有设置属性集,以及点击事件的方法,就要到之后的博客中了。

你可能感兴趣的:(Android自定义View--ClockView)