美文网首页Android
Android自定义View

Android自定义View

作者: goforlondon | 来源:发表于2016-04-29 09:40 被阅读0次

    最近做项目碰到一个需求,需要实现如下图的一个自定义的view。


    icon.jpg

    如上图,还需实现一个动画效果,中间的圆形image保持不动,外面的两个圆弧与圆点沿圆心转动,当触摸view时,整个view等比例放大,转动速度变快,触摸取消时恢复原样。
    自己本身对Android自定义View的实现并不是很熟练,在这个View上卡住了。在后面师傅实现了这个View的所有功能后,自己将代码仔细学习了一遍,有种豁然开朗的感觉。
    首先可以看到,我们这个View应该是直接继承View,看上去貌似是一个组合的view,由中间的ImageView加上周边旋转的圆弧组成,然而仔细考虑后发现,如果采用组合View的写法,可能会有缩放比例方面的问题,所以我们选择了直接继承View来写。
    首先我们分析一下这个View,它由中间的圆形View,两个圆环,两个圆点组成,我们可以记为mAvatar,mGrayRingInner,mGrayRingOuter,mGreenCircle,mRedCircle。其实思路很简单,我们先在自定义View的构造函数里面确定各个部分的长宽参数、转动速度以及触摸事件,然后在重写onDraw()函数进行各个部分的绘制。
    那么我们的实现过程有下列两个部分:

    • 构造函数中确定各数值
    • onDraw()中进行绘制

    GradientDrawable

    在进行这两部分工作之前,我们先介绍一个GradientDrawable这个类。
    GradientDrawable是Drawable类的直接子类,而Drawable类又是个什么类呢?

    官方文档是这样形容的

    A Drawable is a general abstraction for "something that can be drawn." Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms. Unlike a View, a Drawable does not have any facility to receive events or otherwise interact with the user.

    意思是,Drawable首先是一个abstract类,它表示“可以被绘制的一些事情”。经常我们会认为Drawable是能够绘制到屏幕上的资源。但是Drawable跟View不一样,它无法获取事件,同时也无法与用户发生任何的交互。
    那么,既然GradientDrawable是Drawable的直接子类,那么GradientDrawable也能够被绘制。我们同样看一下它的官方介绍。

    A Drawable with a color gradient for buttons, backgrounds, etc.

    可以看出来GradientDrawable是一般被用来绘制按钮、背景等。GradientDrawable可以绘制如下形状。
    int LINE Shape is a line
    int LINEAR_GRADIENT Gradient is linear (default.)
    int OVAL Shape is an ellipse
    int RADIAL_GRADIENT Gradient is circular.
    int RECTANGLE Shape is a rectangle, possibly with rounded corners
    int RING Shape is a ring.
    int SWEEP_GRADIENT Gradient is a sweep.
    那么,我们如何使用GradientDrawable呢?一般来讲,我们通过四个函数实现对GradientDrawable的使用。

    1. setShape() 设置形状,可以设置如上述七种参数
    2. setColor() 设置GradientDrawable的颜色
    3. setBound() 设置GradientDrawable的边界,其实可以理解为我们这个Drawable的大小。
    4. GradientDrawable.draw(canvas) 最后是将其绘制到画布canvas上去。

    构造函数中的数值确定

    在设计中,我们整个View的大小为95dp*95dp,mOuterRing的直径大小为85p,mInnerRing的直径为75dp,mAvatar直径为65dp。这部分直接看代码吧。

    mViewSize = 95dp;
    mGrayRingInner = new GradientDrawable();
    mGrayRingInner.setShape(GradientDrawable.OVAL);
    mGrayRingInner.setStroke(2, getResources().getColor(R.color.divider));
    
    mGrayRingOuter = new GradientDrawable();
    mGrayRingOuter.setShape(GradientDrawable.OVAL);
    mGrayRingOuter.setStroke(2, getResources().getColor(R.color.divider));
    
    mGreenCircle = new GradientDrawable();
    mGreenCircle.setShape(GradientDrawable.OVAL);
    mGreenCircle.setColor(getResources().getColor(R.color.green);
     
    mRedCircle = new GradientDrawable();
    mRedCircle.setShape(GradientDrawable.OVAL);
    mRedCircle.setColor(getResources().getColor(R.color.red));
    initDrawableBounds(); // 设置各个部分的大小
    setOnTouchListener(new OnTouchListener() {
         @Override
         public boolean OnTouch(View v, MotionEvent event) {
         }
    });
    

    onDraw()中的绘制

    首先我们将不转动的部分绘制上去,包括mAvatar, mGrayRingInner和mGrayRingOuter。调用GradientDrawable的draw(canvas)。

    mAvatar.draw(canvas);
    mGrayRingInner.draw(canvas);
    mGrayRingOuter.draw(canvas);
    

    剩下的两部分要实现旋转的动画,我们可以通过不断绘制画布,每一次绘制mGreenCircle与mRedCircle进行相应的坐标变换,这样就可以实现旋转的动画效果。但是,同时我们需要保证mAvatar、mGrayRingInner和mGrayRingOuter不旋转,则需要调用canvas的save()和restore()函数。我们先看看这两个函数的官方说明。

    save(). Saves the current matrix and clip onto a private stack.

    保存当前的matrix放置到一个私有的栈中去。

    restore() .This call balances a previous call to save(), and is used to remove all modifications to the matrix/clip state since the last save call.

    save()和restore()成对出现时,可以有这种效果:保存当前的画布状态,然后你可以进行其他的绘制操作,当调用restore()的时候,移除掉这些修改(新的绘制还存在)。
    所以,我们在绘制mGreenCircle和mRedCircle时,先调用save(),再绘制圆点,再restore()。

    canvas.save();
    canvas.translate(x, y);
    mGreenCircle.draw(canvas);
    canvas.restore();
    // x y 为计算出来的坐标,上面的与下面的并不相同,省去了相关代码
    canvas.save();
    canvas.translate(x, y);
    mRedCircle.draw(canvas);
    canvas.restore();
    

    因为要实现不断的绘制,那么在onDraw()中加上invalidate()。为了保证画布不会多次绘制出现重叠的图案,需要在上述draw操作的外层再增加一层save()和restore()代码。

    @Override
    protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
         canvas.save();
            ...
            ...
            ...
         canvas.restore();
    
         invalidate();
    }
    

    这样便实现了我们需求的自定义View。

    相关文章

      网友评论

        本文标题:Android自定义View

        本文链接:https://www.haomeiwen.com/subject/uqwhrttx.html