一个钟表带你进入Android绘图的世界

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQ243223991/article/details/52300382

前言

顾名思义,就是在Android手机屏幕中绘制我们需要的内容,根据绘制内容的大小(measure),布局(layout)来讲具体内容展示在屏幕中,通过绘制(draw)来实现我们需要的效果.

 

绘图原理(三部曲)

Measure

measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:

(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。

Layout

 layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:

     (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;

     (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;

Draw

draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:

     (1)绘制背景;

     (2)如果要视图显示渐变框,这里会做一些准备工作;

     (3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;

     (4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;

     (5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;

     (6)绘制滚动条;

      从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。

参考:http://blog.csdn.net/xu_fu/article/details/7829721                                     

分类

2D绘图

基于Android SDK内部自己提供,也是我们学习的主要内容,2D绘图的api大部分是android.graphics和android.graphics.drawable包中,他们提供了Canvas,ColorFilter,Point和RetcF等,以及一些动画相关的:AnimationDrawable,BitmapDrawable和TransitionDrawable等.

3D绘图

用Open GL ES 1.0

参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html              

 

2D绘图

内容

通过canvas对象可以绘制的[弧线],[Bitmap],[圆],[点],[矩形],[圆角矩形],[文本],[路径]等图形,其次还提供了旋转(rotate),缩放(scale),渐变(translate)和扭曲(skew)等转换方法.

正文

今天就以2D我学习例子,来完成对钟表的绘制实现.

知识点

1.Paint的创建于使用

2.Canvas的使用

3.Handler使用

4.onMeasure使用

5.Canvas.restore()和Canvas.save的使用

效果图:

 

实现步骤:

1.准备工作,初始化画笔工具,初始化时间,初始化圆环半径

2.画圆环

3.画刻度

4.移动坐标中心到画布中心

5.画指针(画时针,画分针,画秒针)

6.画时间文字

源码解析

第一步|准备工作

private Paint mPaintRing;// 圆环画笔
private Paint mPaintDegree;// 刻度/圆心画笔
private Paint mPaintText;// 文字画笔
private float strokeWidthText = 2;// 文字画笔厚度
private float strokeWidthRing = 4;// 圆环画笔厚度
private float radius = 0;// 圆环半径

//用于初始化时间的角度设置
private float hourDegree = 0;// 时针角度
private float minuteDegree = 0;// 分针角度
private float secondDegree = 0;// 秒针角度

private Date mCurrentDate=null;// 用户设置时间,默认为当前时间
private void initPaint() {
    mCurrentDate=new Date();
    setDate(mCurrentDate);
    // 初始化圆环画笔
    mPaintRing = new Paint();
    mPaintRing.setColor(Color.RED);
    mPaintRing.setStyle(Paint.Style.STROKE);
    mPaintRing.setStrokeWidth(strokeWidthRing);
    mPaintRing.setAntiAlias(true);
    // 初始化刻度画笔
    mPaintDegree = new Paint();
    mPaintDegree.setColor(Color.RED);
    mPaintDegree.setStyle(Paint.Style.FILL);
    mPaintDegree.setAntiAlias(true);
    // 初始化文字画笔
    mPaintText = new Paint();
    mPaintText.setColor(Color.RED);
    mPaintText.setStyle(Paint.Style.FILL);
    mPaintText.setAntiAlias(true);
    mPaintText.setStrokeWidth(strokeWidthText);
}

第二步|绘图

//初始化半径,由于圆环的厚度也要占据一定的宽度,因此需要减除厚度值,这样才能保证圆环
radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
// 1.画圆环
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
// 2.画刻度
drawMark(canvas);
// 3.画圆心
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
// 4.移动坐标中心到画布中心
canvas.save();
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.save();
// 5.画时针
drawHourMark(hourDegree, canvas);
// 6.画分针
drawMinuteMark(minuteDegree, canvas);
// 7.画秒针
drawSecondMark(secondDegree, canvas);
// 8.画时间文字
mPaintText.setTextSize(30);
mPaintText.setTextAlign(Paint.Align.CENTER);
canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);

完整代码

/**
 * 绘图基础-时钟<br>
 * 博客:<a href="http://blog.csdn.net/qq243223991">安前松博客</a>
 */
public class ClockView extends View {

    private Paint mPaintRing;// 圆环画笔
    private Paint mPaintDegree;// 刻度/圆心画笔
    private Paint mPaintText;// 文字画笔
    private float strokeWidthText = 2;// 文字画笔厚度
    private float strokeWidthRing = 4;// 圆环画笔厚度
    private float radius = 0;// 圆环半径

    //用于初始化时间的角度设置
    private float hourDegree = 0;// 时针角度
    private float minuteDegree = 0;// 分针角度
    private float secondDegree = 0;// 秒针角度

    private Date mCurrentDate=null;// 用户设置时间,默认为当前时间


    private  Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==0){
                long times=mCurrentDate.getTime();
                mCurrentDate.setTime(times+1000);
               setDate(mCurrentDate);
                handler.sendEmptyMessageDelayed(0,1000);
            }
        }
    };

    public ClockView(Context context) {
        super(context);
        initPaint();
    }

    public ClockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int min = Math.min(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));// 防止圆形变形,宽高度必须相等
        setMeasuredDimension(min, min);// 重新设置宽高
    }

    /**
     * 根据默认宽度测量宽度
     *
     * @param measureSpec
     * @return
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
     * 根据默认高度测量高度
     *
     * @param measureSpec
     * @return
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private void initPaint() {
        mCurrentDate=new Date();
        setDate(mCurrentDate);
        // 初始化圆环画笔
        mPaintRing = new Paint();
        mPaintRing.setColor(Color.RED);
        mPaintRing.setStyle(Paint.Style.STROKE);
        mPaintRing.setStrokeWidth(strokeWidthRing);
        mPaintRing.setAntiAlias(true);
        // 初始化刻度画笔
        mPaintDegree = new Paint();
        mPaintDegree.setColor(Color.RED);
        mPaintDegree.setStyle(Paint.Style.FILL);
        mPaintDegree.setAntiAlias(true);
        // 初始化文字画笔
        mPaintText = new Paint();
        mPaintText.setColor(Color.RED);
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setAntiAlias(true);
        mPaintText.setStrokeWidth(strokeWidthText);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //初始化半径,由于圆环的厚度也要占据一定的宽度,因此需要减除厚度值,这样才能保证圆环
        radius = Math.min(getWidth() / 2, getHeight() / 2) - strokeWidthRing;
        // 1.画圆环
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, mPaintRing);
        // 2.画刻度
        drawMark(canvas);
        // 3.画圆心
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, mPaintDegree);
        // 4.移动坐标中心到画布中心
        canvas.save();
        canvas.translate(getWidth() / 2, getHeight() / 2);
        canvas.save();
        // 5.画时针
        drawHourMark(hourDegree, canvas);
        // 6.画分针
        drawMinuteMark(minuteDegree, canvas);
        // 7.画秒针
        drawSecondMark(secondDegree, canvas);
        // 8.画时间文字
        mPaintText.setTextSize(30);
        mPaintText.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(XDate.fmtDate(mCurrentDate,"HH:mm:ss"), 0, -20, mPaintText);

    }

    /**
     * 画时针
     *
     * @param degree
     * @param canvas
     */
    private void drawHourMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋转度数
        mPaintDegree.setStrokeWidth(6);
        canvas.drawLine(0, 0, 0, -radius / 2, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 画分针
     *
     * @param degree
     * @param canvas
     */
    private void drawMinuteMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋转度数
        mPaintDegree.setStrokeWidth(3);
        canvas.drawLine(0, 0, 0, -radius / 2 - 25, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 画秒针
     *
     * @param degree
     * @param canvas
     */
    private void drawSecondMark(float degree, Canvas canvas) {
        canvas.rotate(degree);// 旋转度数
        mPaintDegree.setStrokeWidth(2);
        canvas.drawLine(0, 0, 0, -radius / 2 - 40, mPaintDegree);
        canvas.restore();
        canvas.save();
    }

    /**
     * 画刻度
     */
    private void drawMark(Canvas canvas) {
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {
                mPaintDegree.setStrokeWidth(strokeWidthRing);
                canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 40, mPaintDegree);
                mPaintText.setTextAlign(Paint.Align.CENTER);
                mPaintText.setTextSize(20);
                if (i / 5 == 0) {
                    canvas.drawText("12", getWidth() / 2, 60, mPaintText);
                } else {
                    canvas.drawText(i / 5 + "", getWidth() / 2, 60, mPaintText);
                }
            } else {
                mPaintDegree.setStrokeWidth(2);
                canvas.drawLine(getWidth() / 2, strokeWidthRing, getWidth() / 2, 30, mPaintDegree);
            }
            canvas.rotate(6, getWidth() / 2, getHeight() / 2);
        }
    }

    /**
     * 设置时间
     *
     * @param date
     */
    public void setDate(@NonNull Date date) {
        mCurrentDate=date;
        float hourDegree = getHourDegree(getHours(date), getMinutes(date), getSeconds(date));
        float minuteDegree = getMinuteDegree(getMinutes(date), getSeconds(date));
        float secondDegree = getSecondDegree(getSeconds(date));
        rotate(hourDegree, minuteDegree, secondDegree);
    }

    /**
     * 设置时间戳
     *
     * @param timeMills
     */
    public void setTimeMills(long timeMills) {
        Date date = new Date();
        date.setTime(timeMills);
        setDate(date);
    }

    /**
     * 开始时间旋转
     */
    public void start(){
        handler.sendEmptyMessageDelayed(0,1000);
    }

    /**
     * 旋转
     */
    private  void rotate(float hourDegree, float minuteDegree, float secondDegree) {
        this.hourDegree = hourDegree;
        this.minuteDegree = minuteDegree;
        this.secondDegree = secondDegree;
        invalidate();
    }


    /**
     * 根据小时获取角度
     *
     * @param hour
     * @return
     */
    private float getHourDegree(int hour, int minute, int second) {
        float hourDegree = (hour + minute / 60.0f + second / 3600.0f) * 30;
        return hourDegree;
    }

    /**
     * 根据分钟获取角度
     *
     * @param minute
     * @return
     */
    private float getMinuteDegree(int minute, int second) {
        float minuteDegree = (minute + second / 60.0f) * 6;
        return minuteDegree;
    }

    /**
     * 根据秒钟获取角度
     *
     * @param second
     * @return
     */
    private float getSecondDegree(int second) {
        int secondDegree = second * 6;
        return secondDegree;
    }

    /**
     * 获取时间信息
     *
     * @param date
     * @return
     */
    private HashMap<Integer, Integer> getTime(Date date) {
        HashMap<Integer, Integer> time = new HashMap<>();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        time.put(Calendar.HOUR, calendar.get(Calendar.HOUR));//12小时制
        time.put(Calendar.MINUTE, calendar.get(Calendar.MINUTE));
        time.put(Calendar.SECOND, calendar.get(Calendar.SECOND));
        return time;
    }

    /**
     * 获取当天中小时
     *
     * @param date
     * @return
     */
    private int getHours(Date date) {
        return getTime(date).get(Calendar.HOUR);
    }

    /**
     * 获取分钟
     *
     * @param date
     * @return
     */
    private int getMinutes(Date date) {
        return getTime(date).get(Calendar.MINUTE);
    }

    /**
     * 获取秒钟
     *
     * @param date
     * @return
     */
    private int getSeconds(Date date) {
        return getTime(date).get(Calendar.SECOND);
    }


}

觉得有用就支持一下,谢谢!

猜你喜欢

转载自blog.csdn.net/QQ243223991/article/details/52300382
今日推荐