自定义控件-组合控件

自定义控件

当原生控件不能满足需要时,需要进行自定义控件,自定义控件可分为三种方式:

  • 对现有控件进行拓展
  • 通过现有控件的组合实现新的控件
  • 完全自定义一个新的控件

组合控件的使用方式

1、新建控件类

首先控件是一个Java类,如android.widget.TextView类、android.widget.LinearLayout类等,那么自定义控件时,也必须新建一个自定义控件的类。由于该自定义控件由多个现有控件组合而来,该类通常继承一个合适的ViewGroup(ViewGroup可以包含子控件而View不可以),如LinearLayoutRelativeLayout 等。

public class SettingItemView extends RelativeLayout {
    public SettingItemView(Context context, AttributeSet attrs) {
            super(context, attrs);
    }
}

选择构造方法时,应当选择带有AttributeSet attrs参数的构造方法,从attrs参数中可以获取到控件的属性。

  • 控件的属性:以TextView为例,android:layout_widthandroid:layout_heightandroid:textandroid:textSize等在XML文件中使用的,都是控件的属性。自定义控件时可以根据需要设置一些新的属性。


2、新建XML文件

自定义的控件由多个现有控件组合而来,那么就要在XML文件中设置这些现有控件的种类和布局。

控件布局

如图控件采用相对布局,组合了两个TextView和一个CheckBox。

3、控件类加载XML文件

使用如下代码将XML文件填充为View视图:

View view = View.inflate(context, R.layout.setting_item, this);

注意inflate方法第三个参数,设置当前视图的父节点:

ViewGroup root:A view group that will be the parent.

应该记得在ListViewAdapter继承BaseAdapter后实现getView方法中同样使用了这行代码,在Adapter中不需要指定每一个条目的父节点。所以传参数null:

View view = View.inflate(context, R.layout.setting_item, null);

在自定义控件类中,需要指定ViewGroup父节点,由于当前类SettingItemView类继承自RelativeLayout,那么也继承自ViewGroup,所以SettingItemView可以作为ViewGroup类型的父节点,那么就传入this。

也可以使用如下代码,效果完全相同:

View view = View.inflate(context, R.layout.setting_item, null);
this.addView(view);

此时新的自定义控件已经可以在Activity对应的XML布局文件中使用

<com.test.myapplication.view.SettingItemView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
</com.test.mobilesafe.view.SettingItemView>

4、自定义属性

如果新控件需要设置新的属性,那么在values包下新建attrs.xml文件,并写入如下代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="自定义控件类名">
        <attr name="自定义属性名1(eg:item_title)" format="该自定义属性值的类型1"/>
        <attr name="自定义属性名2(eg:item_updata_on)" format="该自定义属性值的类型2"/>
        <attr name="自定义属性名3(eg:item_updata_off)" format="该自定义属性值的类型3"/>
    </declare-styleable>
</resources>

自定义控件类名是否需要带上包名根据编译器提示决定吧 - -

获得自定义属性值:在1、中提到过,自定义控件类的构造方法AttributeSet attrs即携带了自定义属性的值。可通过attrs.getAttributeValue方法获取。第一个参数为命名空间,第二个参数为属性名。即根据命名空间、属性名获取到属性值。

定义属性的目的是通过属性来设置自定义的控件,所以下一步要将获取到的属性值设置到控件上,这里根据具体情况进行具体设置。例如自定义控件包含两个TextView,在布局文件中使用自定义的属性设置了TextView的默认显示文本,则需调用tv1.setText(attr1); tv2.setText(attr2);等进行设置。

5、在布局文件中使用控件的自定义属性

在布局文件中都可以看到这行代码指定了命名空间:

xmlns:android="http://schemas.android.com/apk/res/android" 所以属性可以写成:

android:id="@+id/siv_updata"
android:layout_width="match_parent"
android:layout_height="wrap_content"

那么在类似的,指定 xmlns:mywidget="http://schemas.android.com/apk/res-auto"后,自定义属性也可以类似设置:

mywidget:item_title="何时升级"
mywidget:item_updata_on="现在升级"
mywidget:item_updata_off="今晚升级"

其中mywidget可以任意设置,只需保证命名空间中和属性中的一致即可。


  • 疑问:为什么要指定命名空间:

参考:《属性资源与Android命名空间》:http://my.oschina.net/u/255456/blog/519379

简单解释:需要通过命名空间来指定使用哪一个R文件。
使用attrs.xml自定义属性后,AndroidStudio会在R文件中生成相应属性的索引,在Android自身的R文件中并没有这个属性的索引。同样的,Android原生控件如TextView的属性在应用生成的R文件中没有,而在Android自身的R文件中有。

public static final int SettingItemView_item_des_off = 2;

应用生成的R文件                  Android自身

左图为应用生成的R文件,含有自定义属性索引。
右图为Android自身的R文件,不含有自定义属性索引。

  • 疑问: View view = View.inflate(context, R.layout.setting_item, this); 要指定ViewGroup root为this的原因?

    (待解决)

  • 动态添加组合控件:《Android群英传》- 3.6 节

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值