Android自定义View实现简介

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

 

1.创建自己的view类

通过继承Android的view类或view的子类来创建我们自己的view类。下面我们通过继承ImageButton类实现一个简单的自定义ImageBtn,在这个类种我们会添加3个自定义属性,分别用来定义Button的正常状态,press和focus状态,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 添加如下属性:

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <declare-styleable name="ImageBtn">
        <attr name="imageNormal" format="integer"></attr>
        <attr name="imagePressed" format="integer"></attr>
        <attr name="imageDisabled" format="integer"></attr>
    </declare-styleable>
</resources>

 

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

4.重写onMesure

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

5.重写onDraw

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

6.自定义view使用实例

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <com.test.ImageBtn
         android:id="@+id/ibtn_test"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:imageNormal="@drawable/btn_n"
         app:imagePressed="@drawable/btn_f"/>
</LinearLayout>

上面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:为view或view树计算实际所需的位置大小信息

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

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值