首先这个控件的布局很简单(中间为标题文字,左右各有一个按钮)
但是这个类似的布局很常用,所以就想把它封装起来,通过这个封装来学习以下三点
1.创建了一个常用的复合控件,标题栏
2.为该控件添加自定义的属性值
3.为该控件添加自定义的接口
首先我们要先想好该控件的自定义属性
在res/values下创建 attrs.xml文件 写入以下代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TopBar">
<attr name="titleText" 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>
我们在<declare-styleable name="自定义控件名">中声明是哪个控件的自定义属性,这里我命名为TopBar
<attr name="属性名" format="属性格式">
format可以有以下属性
1.reference: 某一资源的id
2.color : 颜色值
3.boolean: 布尔值
4.dimension: 尺寸值
5.float: 浮点值
6.integer:整形值
7.sting: 字符串
8.fraction: 百分数
9.enum:枚举值
10.flag:位或运算
至于我定义的属性用途,从名字就可以直接的看出来,不再赘述
然后就是关键的TopBar这个类的代码了,我们用TopBar继承RelativeLayout
package com.example.topbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.RelativeLayout.LayoutParams;
import android.view.View.OnClickListener;
public class TopBar extends RelativeLayout implements OnClickListener{
//定义与attrs.xml中的自定义属性对应的属性
private int mLeftTextColor;
private Drawable mLeftBackground;
private String mLeftText;
private int mRightTextColor;
private Drawable mRightBackground;
private String mRightText;
private String mTitleText;
private int mTitleTextColor;
private float mTitleTextSize;
/*
* 定义我们要显示的布局
* 一个显示标题的TextView 两个分居两侧的标题
*/
private Button mLeftButton;
private Button mRightButton;
private TextView mTitleView;
//用于控制控件的布局属性
private LayoutParams mLeftParams;
private LayoutParams mRightParams;
private LayoutParams mTitleParams;
//我们给button赋予一个id,便于在事件监听时候对应到相应button
private final int mLeftButtonId = 1;
private final int mRightButtonId = 2;
//下面我们在这个类中定义的一个公共接口
private OnTopBarClickListener onTopBarClickListener;
//构造函数,attrs就是布局文件传过来的对应的属性
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context,attrs);
initView(context);
}
//将attrs.xml中定义的属性值存入typedArray中
private void initAttrs(Context context, AttributeSet attrs){
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.TopBar);
mLeftTextColor = ta.getColor(
R.styleable.TopBar_leftTextColor,0);
mLeftBackground = ta.getDrawable(
R.styleable.TopBar_leftBackground);
mLeftText = ta.getString(
R.styleable.TopBar_leftText);
mRightTextColor = ta.getColor(
R.styleable.TopBar_rightTextColor,0);
mRightBackground = ta.getDrawable(
R.styleable.TopBar_rightBackground);
mRightText = ta.getString(
R.styleable.TopBar_rightText);
mTitleTextSize = ta.getDimension(
R.styleable.TopBar_titleTextSize, 10);
mTitleTextColor =ta.getColor(
R.styleable.TopBar_titleTextColor, 0);
mTitleText = ta.getString(
R.styleable.TopBar_titleText);
ta.recycle();
}
//初始化布局中的控件,并将属性分配给他们,以及给他们注册监听事件
private void initView(Context context){
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mRightTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitleText);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
mLeftButton.setId(mLeftButtonId);
mLeftParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
mLeftParams.addRule(RelativeLayout.CENTER_VERTICAL);
addView(mLeftButton,mLeftParams);
mRightButton.setId(mRightButtonId);
mRightParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
mRightParams.addRule(RelativeLayout.CENTER_VERTICAL,TRUE);
addView(mRightButton,mRightParams);
mTitleParams = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
mTitleParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
addView(mTitleView,mTitleParams);
mLeftButton.setOnClickListener(this);
mRightButton.setOnClickListener(this);
}
/**
*
* @param id id=0标识leftButton else 标识着rightButton
* @param flag flag = 0 意味着不显示该id控件
* button默认为显示
*/
public void setButtonVisable(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);
}
}
}
//重写借口的onclick方法,去调用自定义的接口的方法
@Override
public void onClick(View v) {
switch(v.getId()){
case mLeftButtonId:
onTopBarClickListener.leftClick();
break;
case mRightButtonId:
onTopBarClickListener.rightClick();
break;
}
}
//为外部增加一个方法去添加接口的监听事件
public void setOnTopBarClickListener(OnTopBarClickListener onTopBarClickListener){
this.onTopBarClickListener = onTopBarClickListener;
}
//自定义一个接口,供使用者为两个button添加事件
public interface OnTopBarClickListener{
void leftClick();
void rightClick();
}
}
然后就是我们定义一个布局,去引用这个自定义控件
activity_topbar_test.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.topbar.TopBarTest"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.example.topbar.TopBar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#ffccff"
custom:leftText="Back"
custom:leftTextColor="#000000"
custom:leftBackground="#ff0000"
custom:rightText="More"
custom:rightTextColor="#000000"
custom:rightBackground="#ff0000"
custom:titleText="自定义标题"
custom:titleTextColor="#123412"
custom:titleTextSize="12sp"
/>
</LinearLayout>
这个地方我们要注意一下,
xmlns:android="http://schemas.android.com/apk/res/android"
是声明android的命名空间,这样我们就可以通过android:来引用其中的属性了
xmlns:custom="http://schemas.android.com/apk/res-auto"
是我们为自定义的属性声明的命名空间,名字为custom
自定义属性的位置是 http://schemas.android.com/apk/res-auto
我们也可以写为 http://schema.android.com/apk/res/包路径
包路径指的是你在Manifest中定义的项目的包路径,而不是说TopBar这个类的包路径
总之就是为了找到我们这个项目,然后正确的识别values 的 attrs.xml
最后就是写一个activity去引用这个布局,然后测试我们是否成功啦
TopBarTestActivity.java
package com.example.topbar;
import custom.TopBar;
import custom.TopBar.OnTopBarClickListener;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.widget.Toast;
public class TopBarTestActivity extends ActionBarActivity {
TopBar topBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去掉系统标题栏
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_topbar_test);
topBar = (TopBar)findViewById(R.id.topBar);
//为topBar中的按钮注册监听事件
topBar.setOnTopBarClickListener(new OnTopBarClickListener(){
@Override
public void leftClick() {
Toast.makeText(TopBarTestActivity.this, "点击了leftButton", Toast.LENGTH_SHORT).show();
}
@Override
public void rightClick() {
Toast.makeText(TopBarTestActivity.this, "点击了rightButton", Toast.LENGTH_SHORT).show();
}
});
/*
* 可以用setButtonVisable(id,flag)来控制是否显示相应按钮
* 添加以下代码,两个按钮都被隐藏了
* topBar.setButtonVisable(0, false);
* topBar.setButtonVisable(1, false);
*/
}
}
好啦,运行可以看到我开始时候放的图片了!
注释都很详尽,我就不再解释了,有问题可以留言讨论~
这只是一个简单的复合控件,但是我们可以通过这个学习到如何自定义一个复合控件,与为他定制属性和一些接口,方法。
由简单到复杂,下一次,我们可以自定义一个SlidingMenu 然后把自定义属性也定制到里面~
github地址 Wizwizard/TopBar