当给的widget不能满足使用的时候就需要自定义。用xml配置view十分方便。如果我们希望通过xml向view中传递参数,就得多写点东西。下面写的例子是对TextView的一个扩展。TextView提供了设置四个方向图片的功能,但是无法设置图片的大小就不太好了。本文实现了一个新的类TextViewPlus,对TextView进行扩展,让其能够在xml中配置图片大小。
改变图片大小不能在xml中做,但是可以在java代码中做。先取得文本周围四个方向的图片Drawable[],然后分别调用drawable.setBounds()设置大小,最后再放回TextView中。然后问题就出在怎么让类读取xml中信息上了。
代码一共分为3部分:
1. main.xml布局文件; 2. TextViewPlus类; 3. attrs.xml文件,用于让系统识别自定义属性。构建顺序应该是attrs.xml -> TextViewPlus -> main.xml
配置文件attrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="TextViewPlus">
<attr name="left_height" format="dimension" />
<attr name="left_width" format="dimension" />
<attr name="right_height" format="dimension" />
<attr name="right_width" format="dimension" />
<attr name="top_height" format="dimension" />
<attr name="top_width" format="dimension" />
<attr name="bottom_height" format="dimension" />
<attr name="bottom_width" format="dimension" />
</declare-styleable>
</resources>
解释:attrs.xml放在res/values中。第4行的name需要指定使用参数的类。第5~12行写的需要在布局文件中设置的参数。name是名称,format是数据类型。系统会自动生成R文件中的变量。
类TextViewPlus
/**
* 添加可以设置drawable大小的功能
*
* @author Daniel
* @version 创建时间: Jul 26, 2012 5:28:59 PM
* */
public class TextViewPlus extends TextView {
// 需要从xml中读取的各个方向图片的宽和高
private int leftHeight = -1;
private int leftWidth = -1;
private int rightHeight = -1;
private int rightWidth = -1;
private int topHeight = -1;
private int topWidth = -1;
private int bottomHeight = -1;
private int bottomWidth = -1;
public TextViewPlus(Context context) {
super(context);
}
public TextViewPlus(Context context, AttributeSet attrs) {
super(context, attrs);
// super一定要在我们的代码之前配置文件
init(context, attrs, 0);
}
public TextViewPlus(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// super一定要在我们的代码之前配置文件
init(context, attrs, defStyle);
}
/**
* 初始化读取参数
* */
private void init(Context context, AttributeSet attrs, int defStyle) {
// TypeArray中含有我们需要使用的参数
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.TextViewPlus, defStyle, 0);
if (a != null) {
// 获得参数个数
int count = a.getIndexCount();
int index = 0;
// 遍历参数。先将index从TypedArray中读出来,
// 得到的这个index对应于attrs.xml中设置的参数名称在R中编译得到的数
// 这里会得到各个方向的宽和高
for (int i = 0; i < count; i++) {
index = a.getIndex(i);
switch (index) {
case R.styleable.TextViewPlus_bottom_height:
bottomHeight = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_bottom_width:
bottomWidth = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_left_height:
leftHeight = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_left_width:
leftWidth = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_right_height:
rightHeight = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_right_width:
rightWidth = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_top_height:
topHeight = a.getDimensionPixelSize(index, -1);
break;
case R.styleable.TextViewPlus_top_width:
topWidth = a.getDimensionPixelSize(index, -1);
break;
}
}
// 获取各个方向的图片,按照:左-上-右-下 的顺序存于数组中
Drawable[] drawables = getCompoundDrawables();
int dir = 0;
// 0-left; 1-top; 2-right; 3-bottom;
for (Drawable drawable : drawables) {
// 设定图片大小
setImageSize(drawable, dir++);
}
// 将图片放回到TextView中
setCompoundDrawables(drawables[0], drawables[1], drawables[2],
drawables[3]);
}
}
/**
* 设定图片的大小
* */
private void setImageSize(Drawable d, int dir) {
if (d == null) {
return;
}
int height = -1;
int width = -1;
// 根据方向给宽和高赋值
switch (dir) {
case 0:
// left
height = leftHeight;
width = leftWidth;
break;
case 1:
// top
height = topHeight;
width = topWidth;
break;
case 2:
// right
height = rightHeight;
width = rightWidth;
break;
case 3:
// bottom
height = bottomHeight;
width = bottomWidth;
break;
}
// 如果有某个方向的宽或者高没有设定值,则不去设定图片大小
if (width != -1 && height != -1) {
d.setBounds(0, 0, width, height);
}
}
}
解释:那个读取TypedArray是主要部分。你可以把TypedArray当成一个Map<Integer key, Object value>。通过遍历key,来获得各个value。而这个key的值是写在R.stylable中的。而value需要用getDimensionPixelSize()等方法读取,这个和调用数据库从cursor中读取数据有点像。
布局文件main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myview="http://schemas.android.com/apk/res/org.daniel.android.test"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<org.daniel.android.test.TextViewPlus
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableBottom="@drawable/ic_launcher"
android:drawableLeft="@drawable/ic_launcher"
android:drawableRight="@drawable/ic_launcher"
android:drawableTop="@drawable/ic_launcher"
android:text="here is a test"
myview:left_height="30dp"
myview:left_width="30dp" />
</LinearLayout>
解释:主要注意第3行和第17~18行。第3行用于声明我需要使用这个项目自己定义的属性。第17~18行就是使用属性了。这个属性就是在attrs.xml中定义的属性。3行和17~18行的myview是对应的,这个名字可以任意换,只要不是关键词和同时对应就行。