自定义控件

自定义控件

自定义控件一般分为以下几种:

  1. 对现有控件进行拓展;
  2. 通过组合来实现新的控件;
  3. 重写view来实现全新的控件。
创建复合控件的步骤:
  1. 定义属性(在res目录的values目录下创建一个attrs.xml属性定义文件)

属性文件

  1. 创建一个自定义控件,并让它继承自ViewGroup,在构造方法中,通过下面的代码来获取在xml布局文件中自定义的那些属性。

    TypedArray ta = context.obtainStyledAttributes(attr,R.styleable.TopBar);

  2. 通过TypedArray对象的getString()、getColor()等方法,就可以获取这些定义的属性值。

    mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor);

当获取完所有的属性值后,需要调用TypeArray的ta.recyle();来完成资源的回收。

  1. 进行控件的组合,为创建的组件元素赋值。值就来源于我们在引用的xml文件中给对应属性的赋值。

    xxx.setColor(mLeftTextColor);

  2. 定义接口(比如响应事件等)。

  3. 引用UI模板

在引用前,需要制定引用第三方控件的名字空间。在布局文件中,可以看到如下一行代码:
xmlns:android=”http://schemas.android.com/apk/res/android
这行代码就是指定引用的名字空间xmlns,即xml namespace。这里指定了名字空间为”android”,因此在接下来使用系统属性的时候,才可以使用”android:”来引用Android的系统属性。同样的,如果使用自定义的属性,那么就需要创建自己的名字空间,第三方的控件都使用如下代码来引入名字空间。

xmlns:custom="http://schemas.android.com/apk/res-auto"

接下来在布局中就能引用了:

custom:leftTextColor="#ffffff"

自定义ViewGroup

自定义ViewGroup需要重写onMeasure()方法来对子View进行测量,重写onLayout()方法来确定子View的位置,重写onTouchEvent()来增加响应事件。

@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){

     super.onMeasure(widthMeasureSpec,heightMeasureSpec);
     int count = getChildCount();
     for(int i = 0; i < count; i++){
        View childView = getChildAt(i);
        measureChild(childView,widthMeasureSpec,heightMeasureSpec);
    }
}

在获取了整个ViewGroup的高度以后,就可以通过遍历来设定每个子View需要放置的位置了,直接调用子View的layout()方法,并将具体的位置作为参数传递进去即可。

@Override
protected void onLayout(boolean changed,int l,int t,int r,int b){

//...
for(int i = 0; i < childCount; i++){

    View child = getChildAt(i);
    child.layout(l,top,r,bottom);
}

}

事件拦截机制

  1. 对于ViewGroup(事件传递)

    dispatchTouchEvent——>onInterceptTouchEvent——>onTouchEvent

  2. 对于View(事件传递)

    dispatchTouchEvent——>onTouchEvent

事件传递的返回值:true 拦截,不继续;false 不拦截,继续流程;
事件处理的返回值:true 处理了,不用审核了;false给上级处理。

在事件传递中,只关心onInterceptTouchEvent()方法。而dispatchTouchEvent()方法一般不用改。