您的位置:首页 > 其它

自定义控件(继承系统控件,非自绘)

2016-05-17 18:28 190 查看
1.写一个类继承自已有的控件(比如textview),override其中的构造函数:

public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

}

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

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}


第一个构造函数一般在代码里new这个自定义控件时使用

第二个构造函数是在layout的xml文件中声明这个控件时,自动调用,attrs里包含了在xml中给这个自定义view设置的所有属性,包括自定义属性和系统属性

第三个构造函数不会自动调用,一般在第二个构造函数手动调用这个构造函数,defStyleAttr是一个自定义属性,在theme文件中利用这个自定义属性给自定义view中的自定义属性设置默认值(详见下面讲解)

第三个构造函数,只有在api21以上才能使用,也不会自动调用,一般在第三个或第二个构造函数手动调用,defStyleRes用于在theme文件中给自定义view中的自定义属性设置默认值(详见下面讲解)

2.在xml中声明这个自定义控件,并设置相关属性

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="com.example.zhouyi.userdefineattrsandstyle.MainActivity">

<com.example.zhouyi.userdefineattrsandstyle.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>


这时候,可以在其构造函数中获取这些定义的系统属性:

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs,
new int[]{android.R.attr.text});
String strValue = typedArray.getString(0);
}


obtainStyledAttributes用于在所有属性集合(attrs)中获取我们需要的属性值,第二个参数是一个int数组,里面设置我们需要获取的属性对应的资源id,比如textview的text属性其实是在android的系统资源的属性文件中设置的一个属性

3.使用自定义属性:

首先在res/values底下建立一个attrs.xml文件,指定自定义属性集合的名字,以及里面的属性名和属性格式:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextViewStyle">
<attr name="attr_one" format="string"/>
<attr name="attr_two" format="string" />
<attr name="attr_three" format="string" />
<attr name="attr_four" format="string" />
</declare-styleable>
</resources>


在xml中定义自定义属性的命名空间并设置自定义属性

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:test = "http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="com.example.zhouyi.userdefineattrsandstyle.MainActivity">

<com.example.zhouyi.userdefineattrsandstyle.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
test:attr_one="attr_one from xml"
android:text="Hello World!" />
</RelativeLayout>


自定属性的命名空间名字可以随意,值统一为:http://schemas.android.com/apk/res-auto

在代码中获取自定义属性值:

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyTextViewStyle);

String strValue_one = typedArray.getString(R.styleable.MyTextViewStyle_attr_one);
}


R.styleable.MyTextViewStyle实际上是一个数组,在自动生成的R文件中可以看到:

public static final class attr {
public static final int attr_one=0x7f010000;
public static final int attr_two=0x7f010001;
public static final int attr_three=0x7f010002;
public static final int attr_four=0x7f010003;
}


public static final class styleable {
public static final int[] Customize = {
0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003
};
public static final int MyTextView_attr_one = 0;
public static final int MyTextView_attr_two = 1;
public static final int MyTextView_attr_three = 2;
public static final int MyTextView_attr_four = 3;
}


4.在哪些地方可以给自定义属性赋值,优先级怎么样:

首先,在布局xml文件中可以给自定义属性赋值,如第3点所示

在样式(style)中也可以给自定义属性赋值

在style.xml中加入一个新的style:

<style name="ThroughStyle">
<item name="attr_one">attr one from style</item>
<item name="attr_two">attr two from style</item>
</style>

注意name直接设置自定义属性名,不要加前缀

在布局文件中声明控件的style为这个style:

<com.example.zhouyi.userdefineattrsandstyle.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/ThroughStyle"
android:text="Hello World!" />


获取属性值的代码不变

style中设置的值比布局xml中设置的值优先级低,也就是说如果同时设置,只有布局xml设置的值起作用

还可以在defStyleAttr(参照第三个构造函数)中给自定义属性赋值:

首先在attrs.xml中设置一个自定义属性:

<resources>
<declare-styleable name="MyTextViewStyle">
<attr name="attr_one" format="string"/>
<attr name="attr_two" format="string" />
<attr name="attr_three" format="string" />
<attr name="attr_four" format="string" />
</declare-styleable>

<attr name="MyTextViewStyleRef" format="reference"/>
</resources>

这个属性不要设置到declare-styleable里面,格式为refrence

然后在style.xml中,找到能影响这个自定义属性的theme,在theme中给这个属性设置值,设置的这个值也是一个自定义style,在这个自定义style中,设置自定义属性的值

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="MyTextViewStyleRef">@style/MyTextViewStyleRefValue</item>
</style>

<style name="MyTextViewStyleRefValue">
<item name="attr_one">attr one from theme reference</item>
</style>

获取自定义属性值的代码也要改一下:

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyTextViewStyle,R.attr.MyTextViewStyleRef,0);

String strValue_one = typedArray.getString(R.styleable.MyTextViewStyle_attr_one);
}

obtainStyledAttributes中加入两个参数,后面那个暂时不管(设置为0,表示忽略),前面那个传入用于设置值的自定义属性的资源id

通过defStyleAttr设置的值,优先级比前两个低

还可以通过defStyleRes(参照第四个构造函数)给自定义属性赋值:

首先在styles.xml中定义一个style:

<style name="MyTextViewDefaultStyle">
<item name="attr_one">attr one from default theme</item>
</style>

在代码中获取自定义属性值:

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyTextViewStyle,0,R.style.MyTextViewDefaultStyle);

String strValue_one = typedArray.getString(R.styleable.MyTextViewStyle_attr_one);
}

defStyleAttr设置为0,表示忽略这个设置

defStyleRes的优先级比布局xml和style低,它和defStyleAttr的关系是:仅在defStyleAttr为0或defStyleAttr不为0但Theme中没有为defStyleAttr属性赋值时起作用,否则defStyleRes的设置不起作用

比如既定义了defStyleRes 又指定了defStyleAttr

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="MyTextViewStyleRef">@style/MyTextViewStyleRefValue</item>
</style>

<style name="MyTextViewStyleRefValue">
<item name="attr_two">attr one from theme reference</item>
</style>

<style name="MyTextViewDefaultStyle"> <item name="attr_one">attr one from default theme</item> </style>

public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyTextViewStyle,R.attr.MyTextViewStyleRef,R.style.MyTextViewDefaultStyle);

String strValue_one = typedArray.getString(R.styleable.MyTextViewStyle_attr_one);
}

虽然获取的是attr_one,并且defStyleRes中给attr_one赋值了,但是得到的值还是null

最后,还可以在theme中直接赋值

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>

<item name="attr_one">@string/app_name</item>
</style>

这种设置的优先级最低,而且如果是字符串,要把设置的值放到string资源里,否则会乱码

5.为什么要在这么多地方都可以设置:

在布局文件中对单个自定义控件属性赋值,这样需要每个控件都写一次

使用style赋值,可以在每个控件中写一句style=xxx就可以了

使用defStyleRes,defStyleAttr,theme赋值,直接写在theme文件中,设置activity或application的theme就可以对里面所有自定义控件属性赋值,相当于设置默认值
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: