自定义复合控件并进行封装

本文介绍如何创建一个自定义的Android复合控件——标题栏,包括设置自定义属性和接口。通过在attrs.xml中定义属性,如颜色、尺寸等,然后在TopBar类中继承RelativeLayout,并在布局文件中引用自定义属性。最后,通过测试活动验证控件的正确使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先这个控件的布局很简单(中间为标题文字,左右各有一个按钮)




但是这个类似的布局很常用,所以就想把它封装起来,通过这个封装来学习以下三点

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




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值