需要创建五个文件,以自定义TopBar为例:
attrs.xml文件定义复合空间的xml属性,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 声明自定义TopBar的属性-->
<declare-styleable name="MyTopBar">
<attr name="title" format="string"/>
<attr name="titleTextSize" format="dimension"/>
<attr name="titleTextColor" format="color"/>
<attr name="leftTextColor" format="color"/>
<attr name="leftBackground" format="reference|color"/>
<attr name="leftText" format="string"/>
<attr name="rightTextColor" format="color"/>
<attr name="rightBackground" format="reference|color"/>
<attr name="rightText" format="string"/>
</declare-styleable>
</resources>
MyTopBar.java文件包括以下内容:
创建组合控件:创建组合控件类,通常需要继承一个合适的ViewGroup;
获取自定义的属性集(attrs.xml):创建TypedArray类来获取属性集,获取完属性集后需要调 用recycle()方法来避免重新创建的时候的错误;
创建组合控件的子控件并赋值;
创建子控件布局元素LayoutParams:为LayoutParams赋值,并将子控件和子控件布局元素通过addView()添加到ViewGroup中;
定义接口对象:实现回调机制,在回调方法中通过映射的接口对象调用接口中的方法而不用去考虑如何实现,具体的实现由调用者去创建;
定义点击事件:按钮的点击事件,不需要具体的实现,只需调用接口的方法,回调的时候,会有具体的实现;
定义一个public方法给调用者来注册接口回调:通过接口来获得回调者对接口方法的实现,重载接口对象中的方法,如按钮的点击事件;
具体代码如下:
package com.example.administrator.customwidget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.view.View;
public class MyTopBar extends RelativeLayout{
// 包含topbar上的控件:左按钮、右按钮、标题
private Button mLeftButton,mRightButton;
private MyTextView mTitleTextView;
// 左按钮的属性值,即我们在attrs.xml文件中定义的属性
private int mLeftTextColor;
private Drawable mLeftBackground;
private String mLeftText;
// 右按钮的属性值,即我们在attrs.xml文件中定义的属性
private int mRightTextColor;
private Drawable mRightBackground;
private String mRightText;
// 标题的属性值,即我们在attrs.xml文件中定义的属性
private float mTitleTextSize;
private int mTitleTextColor;
private String mTitle;
// 布局属性,用来控制组件元素在ViewGroup中的位置
private LayoutParams mLeftParams, mTitleParams, mRightParams;
// 映射传入的接口对象
private topBarClickListener mListener;
//构造方法
public MyTopBar(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
public MyTopBar(Context context){
super(context);
}
public MyTopBar(Context context,AttributeSet attrs){
super(context,attrs);
// 设置topbar的背景
setBackgroundColor(0xFFF59563);
// 通过这个方法,将你在atts.xml中定义的declare-styleable
// 的所有属性的值存储到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.MyTopBar);
// 从TypedArray中取出对应的值来为要设置的属性赋值
mLeftTextColor = ta.getColor(R.styleable.MyTopBar_leftTextColor,0);
mLeftBackground = ta.getDrawable(R.styleable.MyTopBar_leftBackground);
mLeftText = ta.getString(R.styleable.MyTopBar_leftText);
mRightTextColor = ta.getColor(R.styleable.MyTopBar_rightTextColor,0);
mRightBackground = ta.getDrawable(R.styleable.MyTopBar_rightBackground);
mRightText = ta.getString(R.styleable.MyTopBar_rightText);
mTitle = ta.getString(R.styleable.MyTopBar_title);
mTitleTextColor = ta.getColor(R.styleable.MyTopBar_titleTextColor,0);
mTitleTextSize = ta.getDimension(R.styleable.MyTopBar_titleTextSize,10);
// 获取完TypedArray的值后,一般要调用
// recyle方法来避免重新创建的时候的错误
ta.recycle();
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleTextView = new MyTextView(context);
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mRightTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleTextView.setText(mTitle);
mTitleTextView.setTextColor(mTitleTextColor);
mTitleTextView.setTextSize(mTitleTextSize);
mTitleTextView.setGravity(Gravity.CENTER);
mLeftParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT );
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
addView(mLeftButton,mLeftParams);
mRightParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
addView(mRightButton,mRightParams);
mTitleParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
mTitleParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
addView(mTitleTextView,mTitleParams);
// 按钮的点击事件,不需要具体的实现,
// 只需调用接口的方法,回调的时候,会有具体的实现
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.rightClick();
}
});
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.leftClick();
}
});
}
// 暴露一个方法给调用者来注册接口回调
// 通过接口来获得回调者对接口方法的实现
public void setOnTopBarClickListener(topBarClickListener mListener){
this.mListener = mListener;
}
/**
* 设置按钮的显示与否 通过id区分按钮,flag区分是否显示*
* @param id id
* @param flag 是否显示
*/
public void setButtonVisiable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
} else {
mRightButton.setVisibility(View.VISIBLE);
}
} else {
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
} else {
mRightButton.setVisibility(View.GONE);
}
}
}
// 接口对象,实现回调机制,在回调方法中
// 通过映射的接口对象调用接口中的方法
// 而不用去考虑如何实现,具体的实现由调用者去创建
public interface topBarClickListener{
// 左按钮点击事件
void leftClick();
// 右按钮点击事件
void rightClick();
}
/*
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed,l,t,r,b);
}
*/
}
topbar.xml文件中引用复合组件
定义第三方控件命名空间:xmlns:app ="http://schemas.android.com/apk/res-auto",“”中的文字不能错,不然无法获取attrs.xml中自定义的属性;
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<com.example.administrator.customwidget.MyTopBar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app ="http://schemas.android.com/apk/res-auto"
android:id = "@+id/my_TopBar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:leftBackground = "#000000"
app:leftText ="back"
app:leftTextColor = "#FFFFFF"
app:rightBackground = "#000000"
app:rightText ="more"
app:rightTextColor = "#FFFFFF"
app:title = "自定义标题"
app:titleTextSize = "10sp"
app:titleTextColor ="#123412"
>
</com.example.administrator.customwidget.MyTopBar>
activity_main.xml布局文件
1.可以在布局文件中直接应用复合组件;
2.可以 include topbar.xml文件;
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app ="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.administrator.customwidget.MyTopBar
android:id = "@+id/my_TopBar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:leftBackground = "#000000"
app:leftText ="back"
app:leftTextColor = "#FFFFFF"
app:rightBackground = "#000000"
app:rightText ="more"
app:rightTextColor = "#FFFFFF"
app:title = "Title"
app:titleTextSize = "10sp"
app:titleTextColor ="#123412"
>
</com.example.administrator.customwidget.MyTopBar>
<com.example.administrator.customwidget.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Android2222"
android:textSize="30sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android2222"
android:textSize="30sp"
android:background="@drawable/menu"/>
</LinearLayout>
MainActivity.java文件
重载组合控件的点击事件;
代码如下:
package com.example.administrator.customwidget;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyTopBar myTopBar =(MyTopBar)findViewById(R.id.my_TopBar);
myTopBar.setOnTopBarClickListener(
new MyTopBar.topBarClickListener(){
@Override
public void rightClick(){
Toast.makeText(MainActivity.this,"right",Toast.LENGTH_SHORT).show();
}
@Override
public void leftClick(){
Toast.makeText(MainActivity.this,"left",Toast.LENGTH_SHORT).show();
}
});
myTopBar.setButtonVisiable(0,true);
myTopBar.setButtonVisiable(1,true);
}
}
windowActionBar的移除
如果想移除原先自带的windowActionBar,可以在styles.xml文件中修改Theme;
在Theme中增加如下代码:
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
前面之控件Button的宽高是动态设置的,可以修改代码实现非动态设置:
在attrs.xml中增加如下代码:
<attr name="leftLayoutWidth" format="dimension"/>
<attr name="leftLayoutHeight" format="dimension"/>
在MyTopBar.java中增加如下代码:
private int mLeftLayoutWidth;
private int mLeftLayoutHeight;
mLeftLayoutWidth = ta.getLayoutDimension(R.styleable.MyTopBar_leftLayoutWidth,32);
mLeftLayoutHeight = ta.getLayoutDimension(R.styleable.MyTopBar_leftLayoutHeight,32);
在MyTopBar.java中修改如下代码:
mLeftParams = new LayoutParams(mLeftLayoutWidth, mLeftLayoutHeight );
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT|CENTER_VERTICAL,TRUE);
最后就可以在activity_main.xml中实现修改Button宽高:
app:leftLayoutHeight="32dp"
app:leftLayoutWidth="32dp"