上效果图: 

android 代码怎么设置隐藏桌面应用图标_ide


以下标记一些绘图的核心点:


① 测量自定义view的大小

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = getDimension(DEFAULT_WIDTH, widthMeasureSpec);
    int height = getDimension(DEFAULT_HEIGHT, heightMeasureSpec);
    viewW = width;
    cenP.x = viewW / 2;
    cenP.y = height / 2;
    radius = Math.min(viewW, height) / 2;
    setMeasuredDimension(width, height);
}

private int getDimension(int defaultSize, int measureSpec) {
    int result;
    int measureMode = MeasureSpec.getMode(measureSpec);
    int measureSize = MeasureSpec.getSize(measureSpec);
    if (measureMode == MeasureSpec.EXACTLY) {
        result = measureSize;
    } else if (measureMode == MeasureSpec.AT_MOST) {
        result = Math.min(defaultSize, measureSize);
    } else {
        result = defaultSize;
    }
    return result;
}

自定义控件的一般测量测量方式, 大家温习一下


②获取系统时间,这里我使用的是Calendar,非常方便,小小的不足是返回时间是24制式

private void getTime() {
    Calendar calendar = Calendar.getInstance();
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int min = calendar.get(Calendar.MINUTE);
    int sec = calendar.get(Calendar.SECOND);
    if (hour > 12) {
        hour = hour - 12;
    }
    angelS = 360 * sec / 60f;
    angelM = 360 * min / 60f + sec / 60f;
    angelH = 360 * hour / 12f + 360 * min / (60 * 12f) + 360 * sec / (60 * 60 * 12f);
    angelStartS = angelS;
    angelStartM = angelM;
    angelStartH = angelH;
    dateOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
    dateOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
}

这里获取的系统时间作用:计算进入View时,时针、分针、秒针的角度——也就是一开始的初始角度。【这是最重要的一步】


之后的表针转动问题就相对简单了——获取每帧秒针角度angleS,则每帧分针角度angleM = angleS/60,每帧时针角度angleH = angle/3600。


**这里多说几句: 为了实现指针跟随系统时间,我首先想到的使用Animation,而没有使用线程去一直跑。

因为本自定义控件就是简单实现一个时钟,所有工作都可以交给主线程完成,所以使用Animation更为方便,然后通过设置周期单元duration 和 插值器Interpolator 以及循环属性repeatMode和循环次数数repeatCount,这样一个跟随系统时间,简单方便的逻辑就基本形成。


有了清晰的思路,接下来的工作就按部就班了:


首先,我们来获取绘制表盘背景所需要的Bitmap ,

然后初始化一下绘画的一些相关对象:

private void init() {
    src = BitmapFactory.decodeResource(getResources(), R.drawable.iron_bg);
   
    //初始化绘画相关
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    cenP = new PointF();
    path = new Path();
    Animation animation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            float mAngelS = 24 * 60 * 360 * interpolatedTime;
            angelS = angelStartS + mAngelS;
            angelM = angelStartM + mAngelS / 60f;
            angelH = angelStartH + mAngelS / 3600f;
            invalidate();
        }
    };
    animation.setDuration(24 * 60 * 60 * 1000);//一天24个小时为一个周期
    animation.setRepeatCount(Animation.INFINITE);
    animation.setRepeatMode(Animation.REVERSE);
    animation.setInterpolator(new LinearInterpolator());
    startAnimation(animation);
}



有了这些工作,之后剩下的就是绘制各个图形元素了:

首先绘制背景图,这里因为表盘是圆形的,所以使用到Path,通过画布canvas.clipPath()剪裁已经添加了圆形路径的path,得到我们需要的圆形画布,然后在画布上绘制图片。

【**View 绘画的思路跟ps很相似, 当然了如果你没有使用过ps, 那么更形象的比喻就是,跟裁缝剪裁衣服一样。不同的是,裁缝更多的时候是拼接,而画布则更多的是层叠。】

mPaint.setAntiAlias(true);
path.reset();
path.addCircle(cenP.x, cenP.y, radius, Path.Direction.CW);
canvas.clipPath(path);
canvas.drawBitmap(src, 0, 0, mPaint);
path.close();

绘制好了背景,再来为表盘加一个黑色边框

mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(6.18f * 2);
mPaint.setColor(Color.BLACK);
canvas.drawCircle(cenP.x, cenP.y, radius, mPaint);



接下来是什么? 。。。。。。聪明的你或许想到,接下来就是绘制表盘刻度, 然后是表针...done

但是先让我们绘制日期和星期 —— 因为前面说到了, View的绘制是层叠的,而现实中指针肯定实在日期和星期的上面, 所以必须先绘制出较底层的层次:

//日期
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(0);
mPaint.setTextSize(44);
canvas.drawText(String.valueOf(dateOfMonth), cenP.x + radius / 4, cenP.y + 22, mPaint);
//星期
mPaint.setTextSize(44);
String weekDay = null;
switch (dateOfWeek) {
    case 1:
        weekDay = "SUN";
        break;
    case 2:
        weekDay = "MON";
        break;
    case 3:
        weekDay = "TUE";
        break;
    case 4:
        weekDay = "WED";
        break;
    case 5:
        weekDay = "THU";
        break;
    case 6:
        weekDay = "FRI";
        break;
    case 7:
        weekDay = "SAT";
        break;
    default:
        break;
}
assert weekDay != null;
canvas.drawText(weekDay, cenP.x + radius / 2, cenP.y + 22, mPaint);



那么这里,我们就来到了指针的绘制步骤啦:

//绘制时针
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(20f);
float hLen = radius / 2f;
canvas.drawLine(cenP.x, cenP.y, (float) (viewW / 2f + hLen * Math.sin(angelH * Math.PI / 180f)),
        (float) (radius - hLen * Math.cos(angelH * Math.PI / 180f)), mPaint);

//绘制分针
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(12.5f);
float mLen = 2 * radius / 3f;
canvas.drawLine(cenP.x, cenP.y, (float) (viewW / 2f + mLen * Math.sin(angelM * Math.PI / 180f)),
        (float) (radius - mLen * Math.cos(angelM * Math.PI / 180f)), mPaint);

//绘制秒针
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(5f);
float sLen = 5 * radius / 6f;
canvas.drawLine(cenP.x, cenP.y, (float) (viewW / 2f + sLen * Math.sin(angelS * Math.PI / 180f)),
        (float) (radius - sLen * Math.cos(angelS * Math.PI / 180f)), mPaint);



*** 需要简单说一下,,,因为时、分、秒针长度不同, 则我们先设定各个指针的长度,然后指针长度和设定的画笔大小、颜色, 依据各自的角度绘制出一条线,即我们的指针。

***另外,时、分、秒针的绘制顺序依据 (先——时 - 分 - 秒——后),原因同上,就是个层叠问题哈


为了让三个指针看上去更加真实, 而不是简单的将三根线段堆叠在一起,这里还需要在指针轴心点绘制一个圆点,覆盖在最顶层的秒针上。

这样就像我们真实的手表一样啦——三根针一次层叠,然后一根轴心柱插在交点处,如此很好理解啦, 代码如下:

mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(cenP.x, cenP.y, radius / 30, mPaint);



哈哈,快到站咯...........................

忘了点什么? 

对, 我们的刻度呢? ———— 为了避免遮盖,这里我们最后绘制刻度。   为了简单,我简单的绘制了1——12个文字,没有真的绘制出刻度(计算是个大而细的工作,各位可以根据自己喜好去慢慢实现, 原理都简单、相似, 但是过程复杂,呵呵( ̄▽ ̄)")


//12个点
float txtSize = 35f;
mPaint.setTextSize(txtSize);
mPaint.setStrokeWidth(3.09f);
float rR = radius - 35f;
for (int i = 0; i < 12; i++) {
    String txt;
    if (i == 0) {
        txt = "12";
    } else {
        txt = String.valueOf(i);
    }
    canvas.drawText(
            txt
            , (float) (viewW / 2f - txtSize / 2f + rR * Math.sin(i * 30 * Math.PI / 180f))
            , (float) (radius + txtSize / 2f - rR * Math.cos(i * 30 * Math.PI / 180f))
            , mPaint);

}



-----------------------------------------------------------------------------------------------Moses 分割线---------------------------------------------------------------------------------------------------------------


最后,但绝不是最次要的来了。 ——————如何,让我们的指针跑起来——前面说了,我们使用Animation:


Animation animation = new Animation() {
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        float mAngelS = 24 * 60 * 360 * interpolatedTime;
        angelS = angelStartS + mAngelS;
        angelM = angelStartM + mAngelS / 60f;
        angelH = angelStartH + mAngelS / 3600f;
        invalidate();
    }
};
animation.setDuration(24 * 60 * 60 * 1000);//一天24个小时为一个周期
animation.setRepeatCount(Animation.INFINITE);
animation.setRepeatMode(Animation.REVERSE);
animation.setInterpolator(new LinearInterpolator());
startAnimation(animation);


注意一下几点: ①定义一个循环,单元为24小时,也就是一天。————从进入该时钟View开始,各个指针将在24小时后回到进入时的状态(位置、角度)。



所有代码在222行左右, 如果出去添加表盘背景图片, 180行足够。  

以上!