另一个例子:自定义switch按钮
创建复合控件分为三步:
1. 设计属性
2. 实现View
3. 引用View
这里设计一个qq联系人界面的TopBar
其中有三部分组成,左边圆形头像,中间文本,右边按钮,还有个蓝色默认背景
1.设计属性
在Android Studio的res的values中右键新建XML文件qq_topbar_attrs.xml,内容如下
android中通过<declare-styleable>属性声明自定义属性,并通过name设置引用名称
通过<attr>属性声明具体的属性,如文字、标题、颜色,并通过format属性指定属性类型
declare-styleable:告诉系统,以下是我们自定义的属性
attr标签为自定义属性
format为所引用资源类型
Reference为drawable中的文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--1.设计需要的属性-->
<!--自定义属性-->
<declare-styleable name="MyTopBar">
<!--蓝色背景-->
<attr name="topbar_background" format="color|reference"/>
<!--左边头像-->
<attr name="topbar_leftHead" format="color|reference"/>
<!--中间标题-->
<attr name="topbar_title" format="string"/>
<!--左边功能-->
<attr name="topbar_addfriend" format="string"/>
</declare-styleable>
</resources>
第二步:创建自己的View
2.在layout中新建组合控件mytopbat.xml,qq顶部的TopBar
效果:
代码:
<?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="?attr/actionBarSize"
android:orientation="horizontal"
android:background="@color/topbar_colorBlue">
<!--左边头像-->
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/myview_topbar_head"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:src="@mipmap/head"
android:layout_gravity="center_vertical|left"/>
<!--中间标题-->
<TextView
android:id="@+id/myview_topbar_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="联系人"
android:textSize="18sp"
android:textColor="@android:color/white"
android:gravity="center"
android:layout_weight="1"/>
<Button
android:id="@+id/myview_topbar_addfriend"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="添加"
android:textSize="18sp"
android:textColor="@android:color/white"
android:background="@android:color/transparent"
android:layout_gravity="right"/>
</LinearLayout>
3.新建java类MyTopBar并继承一个布局,此处我们使用LinearLayout
然后系统会提示添加构造方法,这儿有四种构造方法,如下:
其中第一个为一般的控件,不需要自定义属性;而自定义属性需要一个Attrs参数,因此选第二个构造方法。
4.然后在java类中声明控件
//定义需要的控件
private CircleImageView headCImage; //头像
private TextView titleTView; //标题
private Button addButton; //添加
5..其次声明所需要的属性
//声明需要的属性
private Drawable background;
private Drawable leftHead;
private String title;
private String addfriend;
6.之后要做的就是赋值,进行控件和属性的关联
在构造方法中获得在attr.xml中自定义的属性,并把属性值赋值给控件
通过TypedArray获得存储在attr.xml中所定义的属性集,如下
//得到自定义属性
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyTopBar);
其中R.styleable.MyTopBar的MyTopBar即在atts的declare-styleable中的name
7.通过TypedArray,就可以获得自定义的属性的值
其中属性名为styleable的名字加下划线,加自定义属性名,如下:
//获得自定义的属性的值
background = typedArray.getDrawable(R.styleable.MyTopBar_topbar_background);
leftHead = typedArray.getDrawable(R.styleable.MyTopBar_topbar_leftHead);
title = typedArray.getString(R.styleable.MyTopBar_topbar_title);
addfriend = typedArray.getString(R.styleable.MyTopBar_topbar_addfriend);
8.TypedArray使用完要进行回收,避免浪费资源,如下:
//使用TypedArray后,要回收资源
typedArray.recycle();
9.之后需要实例化控件,使用动态加载布局的方法
//动态加载布局
View view = LayoutInflater.from(getContext()).inflate(R.layout.mytopbar,this);
//最后实例化控件
initView();
private void initView() {
//最后实例化控件
headCImage = (CircleImageView) findViewById(R.id.myview_topbar_head);
titleTView = (TextView) findViewById(R.id.myview_topbar_title);
addButton = (Button) findViewById(R.id.myview_topbar_addfriend);
}
10.有了控件,之后就需要把自定义属性赋值给控件,如下:
//自定义属性赋值给控件
headCImage.setImageDrawable(leftHead);
titleTView.setText(title);
addButton.setText(addfriend);
//设置背景
setBackground(background);
11.最后就是点击事件了,qq顶部Topbar只有头像和添加可以点击,因此,设置两个监听事件
一般有一下三步完成接口回调机制,
(1) 定义接口,在点击接口中设置点击虚函数,头像点击事件,添加点击事件
//定义点击接口
public interface MyTopBarClickListener{
public void headListener();
public void addListener();
}
定义之后需要声明这个借口的私有对象,以便使用
//声明点击接口对象
MyTopBarClickListener myTopBarClickListener;
(2) 设置一个监听方法,给调用者,参数为接口类型,如下:
//定义设置点击接口方法
public void setMyTopBarClickListener(MyTopBarClickListener listener){
this.myTopBarClickListener = listener;
}
(3) 修改控件点击事件,如下:
private void initEvent() {
//头像点击事件
headCImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
myTopBarClickListener.headListener();
}
});
//添加按钮点击事件
addButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
myTopBarClickListener.addListener();
}
});
}
(4)除了事件的点击接口,我们还可以设置一些共有方法,方便控制,如setText等
//除了事件的点击接口,我们还可以设置一些共有方法,方便控制,如setText等
public void setMyTopBarTitle(String title){
//得到传入的值
this.title = title;
//赋值给控件
titleTView.setText(this.title);
}
第三步:引用自定义View
1. 添加到布局文件,如下:
这里需要注意,怎么使用我们自定义的属性,很简单类型java的import,首先引入我们的自定义控件,
<myView.MyTopBar
android:id="@+id/my_topbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:topbar_background="@color/topbar_colorBlue"
app:topbar_leftHead="@mipmap/head"
app:topbar_title="联系人"
app:topbar_addfriend="添加"/>
然后按住ctrl加左键单击android会弹到上方的
android:layout_width="match_parent"
复制这句话,粘贴在下面,并改为app(或自定义),引用第三方包名的时候只需要把末尾的android替换为res-auto即可,如下,
xmlns:app="http://schemas.android.com/apk/res-auto"
不过不要和系统的android一样
效果:
2.在MainActivity中实例化,并重写点击事件,如下
private MyTopBar myTopbarActivity;
myTopbarActivity = (MyTopBar) findViewById(R.id.my_topbar);
//这样点击头像,标题变为头像,点击添加标题变为添加
myTopbarActivity.setMyTopBarClickListener(new MyTopBar.MyTopBarClickListener() {
@Override
public void headListener() {
Toast.makeText(MyTopbarActivity.this,"头像",Toast.LENGTH_SHORT).show();
myTopbarActivity.setMyTopBarTitle("Click头像");
}
@Override
public void addListener() {
Toast.makeText(MyTopbarActivity.this,"添加",Toast.LENGTH_SHORT).show();
myTopbarActivity.setMyTopBarTitle("Click添加");
}
});
}
最后给出MyTopBar.java的代码:
public class MyTopBar extends LinearLayout {
//定义需要的控件
private CircleImageView headCImage; //头像
private TextView titleTView; //标题
private Button addButton; //添加
//声明需要的属性
private Drawable background;
private Drawable leftHead;
private String title;
private String addfriend;
//声明点击接口对象
MyTopBarClickListener myTopBarClickListener;
public MyTopBar(Context context) {
super(context);
}
public MyTopBar(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(attrs);
initEvent();
}
private void initView() {
//最后实例化控件
headCImage = (CircleImageView) findViewById(R.id.myview_topbar_head);
titleTView = (TextView) findViewById(R.id.myview_topbar_title);
addButton = (Button) findViewById(R.id.myview_topbar_addfriend);
}
private void initEvent() {
//头像点击事件
headCImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
myTopBarClickListener.headListener();
}
});
//添加按钮点击事件
addButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
myTopBarClickListener.addListener();
}
});
}
//进行控件和属性的关联
private void initAttrs(AttributeSet attrs) {
//得到自定义属性
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyTopBar);
//获得自定义的属性的值
background = typedArray.getDrawable(R.styleable.MyTopBar_topbar_background);
leftHead = typedArray.getDrawable(R.styleable.MyTopBar_topbar_leftHead);
title = typedArray.getString(R.styleable.MyTopBar_topbar_title);
addfriend = typedArray.getString(R.styleable.MyTopBar_topbar_addfriend);
//使用TypedArray后,要回收资源
typedArray.recycle();
//动态加载布局
View view = LayoutInflater.from(getContext()).inflate(R.layout.mytopbar,this);
//最后实例化控件
initView();
//自定义属性赋值给控件
headCImage.setImageDrawable(leftHead);
titleTView.setText(title);
addButton.setText(addfriend);
//设置背景
setBackground(background);
}
//定义点击接口
public interface MyTopBarClickListener{
public void headListener();
public void addListener();
}
//定义设置点击接口方法
public void setMyTopBarClickListener(MyTopBarClickListener listener){
this.myTopBarClickListener = listener;
}
//除了事件的点击接口,我们还可以设置一些共有方法,方便控制,如setText等
public void setMyTopBarTitle(String title){
//得到传入的值
this.title = title;
//赋值给控件
titleTView.setText(this.title);
}
}