安卓Android自定义控件:200行代码实现一个简约时钟

11 篇文章 0 订阅

上效果图: 


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


① 测量自定义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小时后回到进入时的状态(位置、角度)。

     ②一个循环完成后,Animation重复repeat起来,这样,一个简约而不无乐趣的时钟控件就完成啦。


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

以上!


附上源码地址: http://download.csdn.net/detail/donmoses/9138165点击打开链接




深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值