安卓系统为我们提供了丰富的控件,但是在实际项目中我们仍然需要重新通过布局来实现一些效果,比如我们需要一个上面图标,下面文字的button,类似于下面这样的:
最直接的解决办法是通过将imageview和textview放在一个垂直排列的LinearLayout中,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:gravity=
"center"
android:orientation=
"vertical"
>
<ImageView
android:id=
"@+id/icon_part"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
/>
<TextView
android:id=
"@+id/text_part"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:textColor=
"#000000"
/>
</LinearLayout>
|
但是每一个button都需要这么长的代码,上面三个按钮的话就需要重复写三次,而且别人一看是个LinearLayout,不会将它button联系起来。
如果有一种办法能将上面那个布局组合成一个控件就好了。
的确是有办法的。主要有两方面的工作。
1.新建一个继承自LinearLayout的类(也可以是其他布局类,不过LinearLayout好像比较合适),然后通过inflater在这个类的构造函数中将上面的布局添加进去。
2.为了能在xml中也给这个自定义控件赋予属性来获得现实效果,比如字体大小、图标资源等,我们还需要在attrs文件中申明一些自定义属性。你可以查阅declare-styleable了解这是怎么回事。
我这里有一个已经实现了这种button效果的类FlexImageButton:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package com.jcodecraeer.client.widget;
import com.jcodecraeer.client.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class FlexImageButton extends LinearLayout {
private ImageView imageView;
private TextView textView;
private CharSequence text;
private Drawable drawable;
private float textSize;
public FlexImageButton(Context context) {
super
(context);
// TODO Auto-generated constructor stub
}
public FlexImageButton(Context context, AttributeSet attrs) {
super
(context, attrs);
// TODO Auto-generated constructor stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlexImageButton);
text = a.getText(R.styleable.FlexImageButton_text);
if
(text==
null
){
text=
""
;
}
Drawable d = a.getDrawable(R.styleable.FlexImageButton_src);
if
(d !=
null
) {
drawable=d;
}
else
{
throw
new
RuntimeException(
"图像资源为空"
);
}
textSize = a.getDimension(R.styleable.FlexImageButton_textSize,12);
String infService = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.flex_image_button_layout,
this
);
imageView = (ImageView) findViewById(R.id.icon_part);
imageView.setImageDrawable(drawable);
textView = (TextView) findViewById(R.id.text_part);
textView.setTextSize((float) textSize);
textView.setText(text);
if
(text.equals(
""
)||text==
null
){
textView.setVisibility(View.GONE);
}
a.recycle();
}
public void setImageResource(int resId) {
imageView.setImageResource(resId);
}
public void setTextViewText(String text) {
textView.setText(text);
}
}
|
在attrs.xml文件中我们声明一些自定义属性,这里我们希望我的FlexImageButton能拥有可以灵活设置的文字属性,字体大小属性、图标资源属性,因此我这样定义:
1
2
3
4
5
6
7
|
<resources>
<declare-styleable name=
"FlexImageButton"
>
<attr name=
"text"
format=
"reference"
/>
<attr name=
"src"
format=
"reference"
/>
<attr name=
"textSize"
format=
"dimension"
/>
</declare-styleable>
</resources>
|
其中format=
"reference"
表示这个属性的值类型是资源id,也就是说在使用FlexImageButton的时候我只可以用资源id来为这个属性赋值。属性值类型有那些,我在文章结尾的附录里面一一列出。
上面我们已经完成了一个自定义的控件,activity的布局文件中如下使用FlexImageButton:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<com.jcodecraeer.client.widget.FlexImageButton
android:layout_height=
"fill_parent"
android:layout_width=
"50dip"
cl:src=
"@drawable/toolbar_collect"
cl:text=
"@string/collect"
android:clickable=
"true"
android:focusable=
"true"
android:layout_marginLeft=
"5dip"
android:layout_marginRight=
"5dip"
android:contentDescription=
"收藏"
android:background=
"@drawable/back_selector"
/>
|
仔细的人会注意到所有这些属性中,有两种形式。其中凡是android:开头的都是系统属性,而
1
2
|
cl:src=
"@drawable/toolbar_collect"
cl:text=
"@string/collect"
|
为我自定义的属性,为什么是cl:开头?
你也可以不用cl开头,但是不管你用什么开头,如果你用到了自定义属性,你都必须在activity布局文件的最开始这样声明:
1
2
3
4
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<RelativeLayout
|
其中cl
就是刚刚用到的,com.jcodecraeer.client
为我的apk包名,注意是apk包名,而不是你自定义控件的在包中的路径。
附录:自定义属性的值类型:
1. reference:参考某一资源ID。
(1)属性定义:
1
2
3
|
<declare-styleable name=
"名称"
>
<attr format=
"reference"
name=
"background"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<ImageView
android:layout_width=
"42dip"
android:layout_height=
"42dip"
android:background=
"@drawable/图片ID"
/>
|
2. color:颜色值。
(1)属性定义:
1
2
3
|
<declare-styleable name=
"名称"
>
<attr format=
"color"
name=
"textColor"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<TextView
android:layout_width=
"42dip"
android:layout_height=
"42dip"
android:textColor=
"#00FF00"
/>
|
3. boolean:布尔值。
(1)属性定义:
1
2
3
|
<declare-styleable name=
"名称"
>
<attr format=
"boolean"
name=
"focusable"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<Button
android:layout_width=
"42dip"
android:layout_height=
"42dip"
android:focusable=
"true"
/>
|
4. dimension:尺寸值。
(1)属性定义:
1
2
3
|
<declare-styleable name=
"名称"
>
<attr format=
"dimension"
name=
"layout_width"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
|
<Button
android:layout_width=
"42dip"
android:layout_height=
"42dip"
/>
|
5. float:浮点值。
(1)属性定义:
1
2
3
4
|
<declare-styleable name=
"AlphaAnimation"
>
<attr format=
"float"
name=
"fromAlpha"
/>
<attr format=
"float"
name=
"toAlpha"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
|
<alpha
android:fromAlpha=
"1.0"
android:toAlpha=
"0.7"
/>
|
6. integer:整型值。
(1)属性定义:
1
2
3
4
|
<declare-styleable name=
"AnimatedRotateDrawable"
>
<attr format=
"integer"
name=
"frameDuration"
/>
<attr format=
"integer"
name=
"framesCount"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<animated-rotate
android:frameDuration=
"100"
android:framesCount=
"12"
/>
|
7. string:字符串。
(1)属性定义:
1
2
3
|
<declare-styleable name=
"MapView"
>
<attr format=
"string"
name=
"apiKey"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<com.google.android.maps.MapView
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:apiKey=
"0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
/>
|
8. fraction:百分数。
(1)属性定义:
1
2
3
4
|
<declare-styleable name=
"RotateDrawable"
>
<attr format=
"fraction"
name=
"pivotX"
/>
<attr format=
"fraction"
name=
"pivotY"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<rotate
android:pivotX=
"200%"
android:pivotY=
"300%"
/>
|
9. enum:枚举值。
(1)属性定义:
1
2
3
4
5
6
|
<declare-styleable name=
"名称"
>
<attr name=
"orientation"
>
<enum name=
"horizontal"
value=
"0"
/>
<enum name=
"vertical"
value=
"1"
/>
</attr>
</declare-styleable>
|
(2)属性使用:
1
2
3
|
<LinearLayout
android:orientation=
"vertical"
>
</LinearLayout>
|
10. flag:位或运算。
(1)属性定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<declare-styleable name=
"名称"
>
<attr name=
"windowSoftInputMode"
>
<flag name=
"stateUnspecified"
value=
"0"
/>
<flag name=
"stateUnchanged"
value=
"1"
/>
<flag name=
"stateHidden"
value=
"2"
/>
<flag name=
"stateAlwaysHidden"
value=
"3"
/>
<flag name=
"stateVisible"
value=
"4"
/>
<flag name=
"stateAlwaysVisible"
value=
"5"
/>
<flag name=
"adjustUnspecified"
value=
"0x00"
/>
<flag name=
"adjustResize"
value=
"0x10"
/>
<flag name=
"adjustPan"
value=
"0x20"
/>
<flag name=
"adjustNothing"
value=
"0x30"
/>
</attr>
</declare-styleable>
|
(2)属性使用:
1
2
3
|
<activity
android:windowSoftInputMode=
"stateUnspecified | stateUnchanged | stateHidden"
>
</activity>
|
注意:属性定义时可以指定多种类型值:
(1)属性定义:
1
2
3
|
<declare-styleable name=
"名称"
>
<attr format=
"reference|color"
name=
"background"
/>
</declare-styleable>
|
(2)属性使用:
1
2
3
4
|
<ImageView
android:layout_width=
"42dip"
android:layout_height=
"42dip"
android:background=
"@drawable/图片ID|#00FF00"
/>
|