轻松学习自定义控件实现表钟效果-进阶篇新手必看,分分钟搞定自定义View

来源:互联网 发布:环保75标准数据 编辑:程序博客网 时间:2024/05/20 00:13

上篇介绍了组合自定义控件,那么今天来实现一个表钟效果。

目前还没有让其动起来。下篇咱们将对其进行完善。咱们完整的效果,既然是钟,那他就得实现秒进分,分进时的功能,还得在刻度上面实现数字显示。


最终的效果是这样的。



其实,实现一个自定义控件其实并不难。在看本篇之前首先推荐大家先看上篇Android实现自定义View之组合控件(一)。

实现一个自定义控件,咱们需要重写的方法有onMeasure()尺寸测量方法,draw()绘画方法,还有构造方法。构造方法我们传入2个参数的就可以了。


首先,我们的思路是这样的。先在draw方法中画置2个空心圆,一个实心圆,还有一条线条。然后再绘画刻度。绘画刻度我是从最左端开始绘画的,学过三角函数的大家应该都有知道,正弦,余弦,正切。

其中的难点就在于计算,其实这些计算也是很简单的,下面我画个丑图给大家解释下如何实现坐标点的计算。

很方便我们就可以拿到刻度点的坐标。我们只需要知道圆的半径和角度,利用正余弦就可以解决。

我们知道一个表钟是360度。那么一个表钟有60个刻度。每个刻度占6度。

首先,我们新建议atttrs.xml文件。这个里面我们暂时只让它有一个改变圆的颜色的属性。


attrs.xml

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name = "Custom">       <attr name="color" format = "color"></attr>    </declare-styleable></resources>

下面我直接上代码,就不一一解释了。看过上一篇的童鞋相信大家能看得懂。


/** *  */package com.mero.customclock.widget;import android.annotation.SuppressLint;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.Display;import android.view.View;import android.view.WindowManager;import com.mero.customclock.R;/** *@项目名称: CustomClock *@文件名称: CustomClockView.java *@Date: 2016-9-25 *@Copyright: 2016 Technology Mero Inc. All rights reserved. *注意:由Mero开发,禁止外泄以及使用本程序于其他的商业目的 。 */public class CustomClockView extends View{private float mScreenWidth;private float graduateStrokeShortLenth = 10;//每一短刻度的值private float graduateStrokeLongLenth = 20;//每一长刻度的值private Paint p;private String TAG;private Rect mBounds;private int color;/** * @param context * @param attrs * @param defStyleAttr */public CustomClockView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public CustomClockView(Context context) {super(context);}/** * @param context * @param attrs */public CustomClockView(Context context, AttributeSet attrs) {super(context, attrs);//得到我们的属性集对象TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.Custom, 0 ,0);color = typedArray.getColor(R.styleable.Custom_color, 0xFF339933);//得到屏幕的宽度WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = wm.getDefaultDisplay();DisplayMetrics metrics  = new DisplayMetrics();display.getMetrics(metrics);mScreenWidth = metrics.widthPixels;}/** *  */private void init(int color) {p = new Paint();p.setAntiAlias(true);//设置为抗锯齿p.setStyle(Paint.Style.STROKE);//设置为线条模式p.setStrokeWidth(5);//设置线条宽度}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);init(color);mBounds = new Rect(0, 0, (int)mScreenWidth/3, (int)mScreenWidth/3);//画大小外圈p.setColor(0xffff3344);canvas.drawCircle(mBounds.width()/2, mBounds.height()/2, 30, p);canvas.drawCircle(mBounds.width()/2, mBounds.width()/2, 100, p);//画中心圈p.setColor(0xFF33ff33);canvas.drawCircle(mBounds.width()/2, mBounds.height()/2, 2, p);p.setColor(0xff229933);//画分针p.setColor(0xffffff44);p.setStrokeWidth(2);canvas.drawLine(mBounds.width()/2,  mBounds.height()/2, mBounds.width()/2+90,  mBounds.height()/2, p);//画刻度drawGraduate(canvas);}/** *  */private void drawGraduate(Canvas canvas) {//计算出初始刻度的坐标位置float w0 = mBounds.width()/2;float h0 = mBounds.height()/2;//创建2只新画笔Paint p1 = new Paint(Paint.ANTI_ALIAS_FLAG);//用来画长刻度Paint p2 = new Paint(Paint.ANTI_ALIAS_FLAG);//用来画短刻度p1.setColor(0xff229944);p1.setStrokeWidth(2);p2.setColor(0xff33ff44);p2.setStrokeWidth(2);/*画短刻度*/for(int i = 0;i<60;i++){float[] a0 = getNewDirect(i, w0, h0,graduateStrokeShortLenth);float[] a1 = getNewDirect(i, w0, h0,graduateStrokeLongLenth);if(i%5==0){canvas.drawLine(w0+(float)(100*(Math.cos(Math.PI/180*i*6))), h0-(float)(100*Math.sin(Math.PI/180*i*6)), a1[0], a1[1], p1);}else{canvas.drawLine(w0+(float)(100*(Math.cos(Math.PI/180*i*6))), h0-(float)(100*Math.sin(Math.PI/180*i*6)), a0[0], a0[1], p2);}}}//计算得到新的尺寸private float[] getNewDirect(int i,float w0,float h0,float graduateStrokeLenth){float w1 = 0,h1 = 0;w1 = (float) (w0+(100+graduateStrokeLenth)*Math.cos(Math.PI/180*i*6));h1 = (float) (h0-(100+graduateStrokeLenth)*Math.sin(Math.PI/180*i*6));return new float[]{w1,h1};}/* (non-Javadoc) * @see android.view.View#onMeasure(int, int) */@SuppressLint("DrawAllocation") @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width = 0;int height = 0;Rect mBounds1 = new Rect(0, 0, (int)mScreenWidth/3, (int)mScreenWidth/3);Log.e(TAG, mBounds1+"");if(widthMode==MeasureSpec.EXACTLY){width = widthSize;Log.e(TAG,"width in exactly: "+width);}else{width = mBounds1.width();Log.e(TAG,"width in other: "+width);}if(heightMode==MeasureSpec.EXACTLY){height = heightSize;Log.e(TAG,"height in exactly: "+height);}else{height = mBounds1.height();Log.e(TAG,"height in other: "+height);}setMeasuredDimension(width, height);}}



在这里咱们需要注意的地方是在测量方法里,由于咱们的自定义不是全屏的控件,所以咱们在使用的时候实际上不希望它自身就是一个match_parent的现象发生。所以在为wrap_content的时候,系统会自动帮我们设置为match_parent。所以此时咱们需要进行测量。也就是测量整个自定义控件的宽高。

在计算完了宽度和高度后,咱们需要使用setMeasuredDimension(width,height)方法将实际测量值设置进去。


实现了之后,咱们在布局文件中就可以使用了。

在布局文件中声明加入控件就可以了。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:mero="http://schemas.android.com/com.mero.customclock"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.mero.customclock.widget.CustomClockView         android:layout_width="wrap_content"        android:layout_height="wrap_content"        mero:color="#FF0000"        android:layout_centerInParent="true"        />   </RelativeLayout>

好了,到这里咱们也就实现了本篇自定义的效果。

1 0
原创粉丝点击