Android开发自定义UI组件
一些复用的UI组件,可以通过设置模板复用,接口回调等方法提高开发效率,降低代码耦合度。
自定义组件大概分为3步:
1、自定义标签属性
2、定义组件类
3、在XML界面布局使用自定义标签
下面举例实现一个TopBar和一个GridItem的自定义组件。
这是项目目录结构
一、自定义标签属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
//TopBar标签属性定义
<declare-styleable name="TopBar">
<attr name="titleTopBar" format="string"></attr>
<attr name="titleTextSize" format="dimension"></attr>
<attr name="titleTextColor" format="color"></attr>
<attr name="leftTextColor" format="color"></attr>
<attr name="leftBackground" format="reference|color"></attr>
<attr name="leftText" format="string"></attr>
<attr name="rightTextColor" format="color"></attr>
<attr name="rightBackground" format=" reference|color"></attr>
<attr name="rightText" format="string"></attr>
</declare-styleable>
//GridItem标签属性定义
<declare-styleable name="GridItem">
<attr name="titlegrid" format="string"></attr>
<attr name="content" format="string"></attr>
<attr name="image" format="reference"></attr>
</declare-styleable>
</resources>
format 参数值:
1. reference:参考某一资源ID
2. color:颜色值
3. boolean:布尔值
4. dimension:尺寸值
5. float:浮点值
6. integer:整型值
7. string:字符串
8. fraction:百分数
9. enum:枚举值
//enum举例 属性定义:
< declare -styleable name = "UI名称" >
<attr name = "orientation" >
<enum name = "horizontal" value = "0" />
<enum name = "vertical" value = "1" />
</attr>
</ declare -styleable>
10. flag:位或运算
二、定义组件类
TopBar组件类
public class TopBar extends RelativeLayout {
//左边Button,中间标题,右边Button
private Button leftBtn, rightBtn;
private TextView tvTitle;
private int leftTextColor;//左边Button字体颜色
private Drawable leftBackground;//左边Button背景色
private String leftText;//左边button文本
private int rightTextColor;
private Drawable rightBackground;
private String rightText;
private float titleTextSize;//标题字体大小
private int titleTextColor;//标题字体颜色
private String title;//标题内容
private LayoutParams leftParams, rightParams, titleParams;
private topBarClickListener listener;
/**
* 接口回调,实现按钮监听模板
*/
public interface topBarClickListener {
public void leftClick();//点击左边按钮监听
public void rightClick();//点击右边按钮监听
}
public void setOnTopBarClickListener(topBarClickListener listener) {
this.listener = listener;
}
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
//用TypedArray获得TopBar属性回传的值
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.TopBar);
//左边按钮属性值
leftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);
leftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
leftText = ta.getString(R.styleable.TopBar_leftText);
//右边按钮属性值
rightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);
rightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);
rightText = ta.getString(R.styleable.TopBar_rightText);
//中间标题属性值
title = ta.getString(R.styleable.TopBar_titleTopBar);
titleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor, 0);
titleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 0);
// 回收,避免浪费资源
ta.recycle();
//此处是创建控件对象,通过LayoutParams设置控件属性,也可使用XML界面布局代码创建
leftBtn = new Button(context);
rightBtn = new Button(context);
tvTitle = new TextView(context);
leftBtn.setTextColor(leftTextColor);
leftBtn.setBackground(leftBackground);
leftBtn.setText(leftText);
rightBtn.setTextColor(rightTextColor);
rightBtn.setBackground(rightBackground);
rightBtn.setText(rightText);
tvTitle.setText(title);
tvTitle.setTextColor(titleTextColor);
tvTitle.setTextSize(titleTextSize);
tvTitle.setGravity(Gravity.CENTER);
setBackgroundColor(0x66E93EFF);
leftParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
leftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
addView(leftBtn, leftParams);
rightParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
rightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
addView(rightBtn, rightParams);
titleParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT);
titleParams.addRule(RelativeLayout.CENTER_IN_PARENT);
addView(tvTitle, titleParams);
leftBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.leftClick();
}
});
rightBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.rightClick();
}
});
}
/**
* 定义左边Button是否可见
*/
public void setLeftButtonVisiable(boolean flag) {
if (flag) {
leftBtn.setVisibility(View.VISIBLE);
} else {
leftBtn.setVisibility(View.GONE);
}
}
/**
* 定义右边Button是否可见
*/
public void setRightButtonVisiable(boolean flag) {
if (flag) {
rightBtn.setVisibility(View.VISIBLE);
} else {
rightBtn.setVisibility(View.GONE);
}
}
/**
* 设置左边Button背景
*/
public void setLeftBackground(int resid) {
leftBtn.setBackgroundResource(resid);
}
/**
* 设置右边Button背景
*/
public void setRightBackground(int resid) {
rightBtn.setBackgroundResource(resid);
}
}
GridItem组件类
public class GridItem extends LinearLayout {
private TextView tvGridTitle, tvGridContent;
private ImageView ivGridIcon;
private View gridView;
private String title;
private String content;
private Drawable imageResId;
public GridItem(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.GridItem);
gridView = LayoutInflater.from(context).inflate(R.layout.grid_item,
this, true);
tvGridTitle = (TextView) gridView.findViewById(R.id.tvGridTitle);
tvGridContent = (TextView) gridView.findViewById(R.id.tvGridContent);
ivGridIcon = (ImageView) gridView.findViewById(R.id.ivGridIcon);
// 获取自定义属性值
title = ta.getString(R.styleable.GridItem_titlegrid);
content = ta.getString(R.styleable.GridItem_content);
imageResId = ta.getDrawable(R.styleable.GridItem_image);
// 回收一下,避免资源浪费
ta.recycle();
tvGridTitle.setText(title);
tvGridContent.setText(content);
ivGridIcon.setImageDrawable(imageResId);
}
}
GridItem布局文件grid_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="3dp"
android:paddingLeft="6dp" >
<TextView
android:id="@+id/tvGridTitle"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="10sp" />
<TextView
android:id="@+id/tvGridContent"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:textSize="10sp" />
<ImageView
android:id="@+id/ivGridIcon"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="right"
android:layout_weight="2"
android:scaleType="fitCenter"
android:scaleX="0.8"
android:scaleY="0.8" />
</LinearLayout>
自定义组件的布局,一般是由简单的组件组合而成,组合成新的自定义组件,可以创建一个单独的布局文件进行定义。
三、在XML界面布局使用自定义标签
activity_main.xml布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<span style="color:#ff0000;">xmlns:app="http://schemas.android.com/apk/res/com.example.topbar"</span>
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.topbar.view.TopBar
android:id="@+id/topbar"
android:layout_width="match_parent"
android:layout_height="48dp"
app:leftTextColor="#fff"
app:rightText="设置"
app:rightTextColor="#fff"
app:titleTextColor="#fff"
app:titleTextSize="13sp"
app:titleTopBar="自定义标题" >
</com.example.topbar.view.TopBar>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/topbar"
android:layout_margin="10dp"
android:orientation="horizontal" >
<com.example.topbar.view.GridItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#66FF0000"
app:content="安卓开发"
app:image="@drawable/grid_icon1"
app:titlegrid="Android" />
<com.example.topbar.view.GridItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="10dp"
android:background="#660000FF"
app:content="编程语言"
app:image="@drawable/grid_icon2"
app:titlegrid="C++" />
<com.example.topbar.view.GridItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#66AA7700"
app:content="服务器"
android:layout_marginLeft="10dp"
app:image="@drawable/grid_icon3"
app:titlegrid="Linux" />
<com.example.topbar.view.GridItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="10dp"
android:background="#66228B22"
app:content="苹果开发"
app:image="@drawable/grid_icon4"
app:titlegrid="Ios" />
</LinearLayout>
</RelativeLayout>
<h4><span style="color:#ff0000;">首先重点是声明组件的命名空间,作用相当于类中引用包名。</span></h4>
xmlns:app="http://schemas.android.com/apk/res/<span style="color:#ff0000;">com.example.topbar</span>
与系统的命名空间比较:
系统:xmlns:android="http://schemas.android.com/apk/res/<span style="color:#ff0000;">android</span>
res/后面的包名不同。
在AndroidStudio中可以设置为这样:
xmlns:custom="http://schemas.android.com/apk/<span style="color:#ff0000;">res-auto</span>