Android 自定义view第二弹——组合控件

来源:互联网 发布:thinkphp nginx 配置 编辑:程序博客网 时间:2024/04/30 05:55

概述:本篇为自定义控件的三种实现方式第一种—组合控件,在此篇中,我将以一个例子的形式来展现组合控件的实现方式。

一组合控件的定义

自定义组合控件一般来说都是以ViewGroup及其子类(LinearLayout、RelativeLayout、FrameLayout等)为主,内部嵌套其他控件,来组合成一个新的控件,实现一些特定的需要,可以是代码简化,结构清晰,重用性较高。通常来说,我们会实现定义好一个Layout.xml文件,然后让我们的自定义控件去加载此xml,并获取子控件,然后设置属性(可以通过代码,也可以从资源文件中加载)、添加事件。

二 自定义组合控件注意要点:

1.加载xml文件是在构造方法中完成的,通过调用inflate(R.layout.my_layout,this,true),注意第二个和第三个参数;

2.如果需要从资源文件中加载自定义的属性,则必须重写Constructor(Context context, AttributeSet attrs)此构造方法,属性是定义在attrs.xml中的;

3.获取子控件对象,可以在构造方法中获取,也可以重写onFinishInflate()方法来获取,个人建议采用第二种,可以保证控件已经完全加载好了;

4.添加事件可以直接在控件中写,不过考虑到扩展性及复用性,建议对外暴露接口。

三 一个栗子

1 栗子说明

在很多项目中,我们都会用到app标题栏,几乎每个页面的形式都一样,左边 中间 右边 。但在每个页面中 左边 中间 右边的内容又是不一样的。如果我们在每个页面的布局中重新写的话,将是一个繁琐的工作。如果我们将所有的形式都融合在一个布局用 include来引用时 每个页面的逻辑不一样 要让不用的控件来显示和隐藏 就会显得特别的麻烦。所以在这里我们用自定义view的组合控件的方式简化我们的代码实现同样的效果。

2 栗子实现步骤

① 继承

自定义组合控件必须继承ViewGroup或者其子类。
CustomTitleBar extends RelativeLayout 在这里我们继承的是RelativeLayout 至于为什么继承RelativeLayout 我会在后面说明。先来看看我们的构造方法

 public CustomTitleBar(Context context) {        this(context, null);    }    public CustomTitleBar(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CustomTitleBar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs, defStyleAttr);    }

一个参数的构造方法 调 二个参数的构造方法 两个参数的构造方法 调三个参数的构造方法 在三个参数的构造方法中 有一个init()的方法初始化 传入了 上下文 属性集合 默认样式

② 自定义属性

在res文件夹下的value文件中 生成 attrs文件
声明一组属性:
使用来定义一个属性集合,name就是属性集合的名字,这个名字一定要起的见名知意。

  <declare-styleable name="CustomTitleBar">    <!--添加属性-->   </declare-styleable>

然后就是定义属性值了,通过 方式定义属性值,属性名字同样也要起的见名知意,format表示这个属性的值的类型,类型有以下几种:

reference:引用资源string:字符串Color:颜色boolean:布尔值dimension:尺寸值float:浮点型integer:整型fraction:百分数enum:枚举类型flag:位或运算

栗子的属性集合如下

<resources>    <declare-styleable name="CustomTitleBar">        <attr name="TitleBar_background_color" format="color|reference" />        <!-- 左边 -->        <attr name="TitleBar_left_text" format="string|reference" />        <attr name="TitleBar_left_text_size" format="dimension|reference" />        <attr name="TitleBar_left_text_color" format="color|reference" />        <attr name="TitleBar_left_Drawable" format="reference" />        <!-- 中间-->        <attr name="TitleBar_center_text" format="string|reference" />        <attr name="TitleBar_center_text_size" format="dimension|reference" />        <attr name="TitleBar_center_text_color" format="color|reference" />        <attr name="TitleBar_center_Drawable" format="reference" />        <!-- 右边-->        <attr name="TitleBar_right_text" format="string|reference" />        <attr name="TitleBar_right_text_size" format="dimension|reference" />        <attr name="TitleBar_right_text_color" format="color|reference" />        <attr name="TitleBar_right_Drawable" format="reference" />    </declare-styleable></resources>

③ 获取自定义属性

init()方法中 首先 初始化布局:

//初始化布局LayoutInflater.from(getContext()).inflate(R.layout.layout_custom_titlebar, this, true);

我们来看看我们的布局文件

<merge xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:id="@+id/left_tv"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_alignParentLeft="true"        android:background="?android:attr/selectableItemBackground"        android:gravity="center_vertical|left"        android:minWidth="50dp"        android:paddingLeft="@dimen/activity_horizontal_margin"        android:paddingRight="10dp"        android:singleLine="true"        android:textColor="@android:color/white"        android:textSize="16sp" />    <TextView        android:id="@+id/center_tv"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_centerHorizontal="true"        android:layout_marginLeft="70dp"        android:layout_marginRight="70dp"        android:gravity="center_vertical"        android:singleLine="true"        android:text=""        android:textColor="@android:color/white"        android:textSize="18sp" />    <TextView        android:id="@+id/right_tv"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_alignParentRight="true"        android:background="?android:attr/selectableItemBackground"        android:gravity="center_vertical|right"        android:minWidth="50dp"        android:paddingLeft="10dp"        android:paddingRight="@dimen/activity_horizontal_margin"        android:singleLine="true"        android:text=""        android:textColor="@android:color/white"        android:textSize="16sp" /></merge>

可以看到在布局文件中 我们用到了 merge标签 不知道什么意思和用法的同学请自行Google(我鄙视百度) 因为为了 更好的控制布局 我们继承了RelativeLayout 也就是 我们把merge标签里的控件直接放在了RelativeLayout中 所以 在merger标签中的 控件 的属性 要按RelativeLayout布局来使用 比如

<TextView        android:id="@+id/left_tv"        android:layout_width="wrap_content"        android:layout_height="match_parent"        **android:layout_alignParentLeft="true"**        android:background="?android:attr/selectableItemBackground"        **android:gravity="center_vertical|left"**        android:minWidth="50dp"        android:paddingLeft="@dimen/activity_horizontal_margin"        android:paddingRight="10dp"        android:singleLine="true"        android:textColor="@android:color/white"        android:textSize="16sp" />
//获取自定义属性的集合TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomTitleBar, defStyleAttr, 0);//背景颜色mBackgroundColor = array.getColor(R.styleable.CustomTitleBar_TitleBar_background_color, Color.WHITE);//左边mLeftText = array.getString(R.styleable.CustomTitleBar_TitleBar_left_text);mLeftTextColor = array.getColor(R.styleable.CustomTitleBar_TitleBar_left_text_color, Color.BLACK);mLeftTextSize = array.getDimension(R.styleable.CustomTitleBar_TitleBar_left_text_size, sp2px(16));mLeftDrawable = array.getDrawable(R.styleable.CustomTitleBar_TitleBar_left_Drawable);//中间mCenterText = array.getString(R.styleable.CustomTitleBar_TitleBar_center_text);mCenterTextColor = array.getColor(R.styleable.CustomTitleBar_TitleBar_center_text_color, Color.WHITE);mCenterTextSize = array.getDimension(R.styleable.CustomTitleBar_TitleBar_center_text_size, sp2px(18));mCenterDrawable = array.getDrawable(R.styleable.CustomTitleBar_TitleBar_center_Drawable);//右边mRightText = array.getString(R.styleable.CustomTitleBar_TitleBar_right_text);mRightTextColor = array.getColor(R.styleable.CustomTitleBar_TitleBar_right_text_color, Color.WHITE);mRightTextSize = array.getDimension(R.styleable.CustomTitleBar_TitleBar_right_text_size, sp2px(16));mRightDrawable = array.getDrawable(R.styleable.CustomTitleBar_TitleBar_right_Drawable);//释放资源 必须添加array.recycle();

④ 获取控件 设置空间属性

/**     * 此方法中 代表布局一定初始化完成 不存在空指针问题     */    @Override    protected void onFinishInflate() {        super.onFinishInflate();        tvLeft = (TextView) findViewById(R.id.left_tv);        tvCenter = (TextView) findViewById(R.id.center_tv);        tvRight = (TextView) findViewById(R.id.right_tv);        //设置背景颜色        setBackgroundColor(mBackgroundColor);        //设置左边        setLeft(mLeftText, mLeftTextColor, mLeftTextSize, mLeftDrawable);        //设置中间        setCenter(mCenterText, mCenterTextColor, mCenterTextSize, mCenterDrawable);        //设置右边        setRight(mRightText, mRightTextColor, mRightTextSize, mRightDrawable);    } private void setLeft(CharSequence leftText, int leftTextColor, float leftTextSize, Drawable leftDrawable) {        if (leftDrawable != null) {            leftDrawable.setBounds(0, 0, leftDrawable.getMinimumWidth(), leftDrawable.getMinimumHeight());            tvLeft.setCompoundDrawables(leftDrawable, null, null, null);        }        tvLeft.setText(leftText);        tvLeft.setTextSize(TypedValue.COMPLEX_UNIT_PX, leftTextSize);        tvLeft.setTextColor(leftTextColor);        tvLeft.setClickable(!TextUtils.isEmpty(leftText) || leftDrawable != null);    }    private void setCenter(CharSequence centerText, int centerTextColor, float centerTextSize, Drawable centerDrawable) {        if (centerDrawable != null) {            centerDrawable.setBounds(0, 0, centerDrawable.getMinimumWidth(), centerDrawable.getMinimumHeight());            tvCenter.setCompoundDrawables(centerDrawable, null, null, null);        }        tvCenter.setText(centerText);        tvCenter.setTextSize(TypedValue.COMPLEX_UNIT_PX, centerTextSize);        tvCenter.setTextColor(centerTextColor);        tvCenter.setClickable(!TextUtils.isEmpty(centerText) || centerDrawable != null);    }    private void setRight(CharSequence rightText, int rightTextColor, float rightTextSize, Drawable rightDrawable) {        if (rightDrawable != null) {            rightDrawable.setBounds(0, 0, rightDrawable.getMinimumWidth(), rightDrawable.getMinimumHeight());            tvRight.setCompoundDrawables(rightDrawable, null, null, null);        }        tvRight.setText(rightText);        tvRight.setTextSize(TypedValue.COMPLEX_UNIT_PX, rightTextSize);        tvRight.setTextColor(rightTextColor);        tvRight.setClickable(!TextUtils.isEmpty(rightText) || rightDrawable != null);    }    /**     * 将空间暴露出去 提供给外界     * @return     */    public TextView getTvLeft() {        return tvLeft;    }    /**     * 将空间暴露出去 提供给外界     * @return     */    public TextView getTvCenter() {        return tvCenter;    }    /**     * 将空间暴露出去 提供给外界     * @return     */    public TextView getTvRight() {        return tvRight;    }    //一些工具方法    protected int dp2px(int dp) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());    }    protected int sp2px(int sp) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());    }

⑤ 引用自定义控件

千万不要忘了在跟节点 标记命名空间

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    ***xmlns:app="http://schemas.android.com/apk/res-auto"***    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.jack.customtitleview.customView.CustomTitleBar        android:layout_marginTop="10dp"        android:layout_width="match_parent"        android:layout_height="48dp"        ***app:TitleBar_background_color="#00ff00"        app:TitleBar_left_text="中国"***/>    <com.jack.customtitleview.customView.CustomTitleBar        android:layout_marginTop="10dp"        android:layout_width="match_parent"        android:layout_height="48dp"        ***app:TitleBar_background_color="#12b7f5"***        ***app:TitleBar_left_Drawable="@drawable/ic_back_gray"        app:TitleBar_left_text="中国"***        ***app:TitleBar_right_Drawable="@drawable/ic_msg_gray"*** />    <com.jack.customtitleview.customView.CustomTitleBar        android:layout_marginTop="10dp"        android:layout_width="match_parent"        android:layout_height="48dp"        ***app:TitleBar_background_color="#12b7f5"***        ***app:TitleBar_left_Drawable="@drawable/ic_back_gray"        app:TitleBar_right_Drawable="@drawable/ic_msg_gray"***>        ***<include layout="@layout/layout_search_view" />***    </com.jack.customtitleview.customView.CustomTitleBar></LinearLayout>

因为 我们继承的RelativeLayout 也是一个容器 所以可以引用 include

四 效果显示

这里写图片描述

源码地址 源码地址

0 0
原创粉丝点击