Android自定义View实现简介

Android系统原生的为我们提供很多的功能强大的基础view控件,但即使如此,很多时候,他们还是不能够满足我们的需求,那么我们就需要通过自定义View来实现自己的view。在Android系统种已经为我们提供了一套很好的机制来实现自定义view,下面我们就自定义view的实现过程做一个简单的介绍。

 

1.创建自己的view

通过继承Androidview类或view的子类来创建我们自己的view类。下面我们通过继承ImageButton类实现一个简单的自定义ImageBtn,在这个类种我们会添加3个自定义属性,分别用来定义Button的正常状态,pressfocus状态,disable状态的图片资源。类的定义如下,initImageBtn方法用来处理自定义属性,后面添加自定属性后我们在完善。

public class ImageBtn extends ImageButton {
    public ImageBtn(Context context) {
        super(context);
    }

    public ImageBtn(Context context, AttributeSet attrs) {
        super(context, attrs);
        initImageBtn(context, attrs, 0);
    }

    public ImageBtn(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initImageBtn(context, attrs, defStyleAttr);
    }

    private void initImageBtn(Context context, AttributeSet attrs, int defStyleAttr){
    }
}


2.为自己的view添加自定属性

Android应用种添加自定义属性,我们只需要在res/values下建立attrs.xml文件,并在里面添加相应的自定义属性即可,此次,我们为ImageBtn 添加如下属性:



 
    
        
        
        
    


上面的attrs.xml文件中,我们为ImageBtn添加了三个自定义属性,分别是imageNormal,imagePressed和imageDisabled;三个属性的格式类型都是integer,此实例中是一个Drewable的资源Id值。


3.在自定义view类种使用自定义属性

上面我们已经为ImageBtn定义了三个自定义属性,下面根据自定义属性来完善我们的ImageBtn类。

定义属性的 format 可以是integer, string, boolean, color, dimension等,具体可以看 ide 的提示(建议使用 Android studio )。

private void initImageBtn(Context context, AttributeSet attrs, int defStyleAttr){

        TypedArray typeds = context.obtainStyledAttributes(attrs, R.styleable.ImageBtn, defStyleAttr, 0);
        int nCount = typeds.getIndexCount();

        Drawable imgNormal = null;
        Drawable imgPressed = null;
        Drawable imgDiabeled = null;

        for (int i = 0; i < nCount; ++i) {
            int attr = typeds.getIndex(i);
            if (attr == R.styleable.ImageBtn_imageNormal){
                imgNormal = typeds.getDrawable(attr);
            } else if (attr == R.styleable.ImageBtn_imagePressed){
                imgPressed = typeds.getDrawable(attr);
            } else if (attr == R.styleable.ImageBtn_imageDisabled){
                imgDiabeled = typeds.getDrawable(attr);
            }
        }

        typeds.recycle();

        StateListDrawable sd = new StateListDrawable();
        if (imgDiabeled != null) {
            sd.addState(new int[]{-android.R.attr.state_enabled}, imgDiabeled);
        }
        if (imgPressed != null){
            sd.addState(new int[]{android.R.attr.state_focused}, imgPressed);
            sd.addState(new int[]{android.R.attr.state_pressed}, imgPressed);
        }
        if (imgNormal != null){
            sd.addState(new int[]{}, imgNormal);
        }

        setBackgroundDrawable(sd);
}

通过context.obtainStyledAttributes方法,我们可以获取到ImageBtn的属性(layout文件种定义的属性)在获取到的TypedArray种,我们通过对比其各个itemindex值来获取我们的自定义属性。获取到的TypedArray需要显示的调用recycle()。获取到自定属性后,我们通过建立一个StateListDrawable,并把各个状态的ImageDrawable添加到StateListDrawable中,最后调用setBackgroundDrawable进行设置。有没有发现,最后这部分tateListDrawable的处理与我们平时使用ImageButton时位他自定义的Background的Drawable很像呢,是的,他们的实现原理是一样,此处只是换了一个表达形似而已。ImageView等根据触摸状态发生变化的部分大多都是通过一StateListDrawable来管理要显示的Drawable,通过StateListDrawable来控制不同的状态显示不同的状态。

4.重写onMesure

onMesure用来测量view的大小,一般父view执行layout的时候都会通过调用子viewonMesure来测量view需要的位置大小。此实例中直接使用父类的onMesure。

5.重写onDraw

onDraw用来实现view的绘制部分,此实例种使用与父类相同的方式来处理Background的跟随状态变化功能,因此此实例中也是直接使用父类的onDraw

6.自定义view使用实例

好了,我们的自定义viewImageBtn写好了,把他加入layout种使用吧。



    

上面layout中我们加如了imageNormal和imagePressed属性,即定义了按下状态的背景图片和正常状态下的背景图片。运行一下,效果如我们期待。

7.View绘制简介

Android系统中,与view绘制流程息息相关的三个重要接口是onLayout,onMesure,onDraw。View控件都是放在ViewGroup中的,整个view的树形结构如下:


整个view树的绘制会经历以下过程Layout->Mesure->Draw,在重绘过程种,如果不需要重新layout和重新Mesure则会直接进行Draw

Layout: 主要是根据子view的大小及布局信息以及ViewGroup自身的特效及属性,将当前View放到父view的合适位置上。

Mesure:为viewview树计算实际所需的位置大小信息

Draw:绘制view,处理绘制业务。




你可能感兴趣的:(Android)