Android自定义控件之自定义组合控件(三)


前言:

     前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一)、自定义属性Android自定义控件之自定义属性(二)。今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发成本,以及维护成本。

使用自定义组合控件的好处?

    我们在项目开发中经常会遇见很多相似或者相同的布局,比如APP的标题栏,我们从三种方式实现标题栏来对比自定义组件带来的好处,毕竟好的东西还是以提高开发效率,降低开发成本为导向的。

1.)第一种方式:直接在每个xml布局中写相同的标题栏布局代码
复制代码
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lee="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:background="@color/green"
        android:layout_height="45dp">

        <Button
            android:id="@+id/title_bar_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="5dp"
            android:background="@mipmap/titlebar_back_icon"
            android:minHeight="45dp"
            android:minWidth="45dp"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/title_bar_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="登录"
            android:singleLine="true"
            android:textSize="17sp" />

        <Button
            android:id="@+id/title_bar_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="7dp"
            android:text="提交"
            android:textColor="@android:color/white"
            android:background="@null"
            android:minHeight="45dp"
            android:minWidth="45dp"
            android:textSize="14sp" />
    </RelativeLayout>

</LinearLayout>
 
复制代码

    这种方式没有任何布局复用的概念,同时也让当前的布局变得臃肿难以维护,开发效率低下,而且这个还需要要求每个开发人员必须细心否则有可能会做出参差不齐的标题栏,所以这种方式是最不推荐使用的。

2.)第二种方式:使用include标签

 首先定义标题栏布局

复制代码
 
    <RelativeLayout
        android:layout_width="match_parent"
        android:background="@color/green"
        android:layout_height="45dp">

        <Button
            android:id="@+id/title_bar_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="5dp"
            android:minHeight="45dp"
            android:minWidth="45dp"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/title_bar_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:singleLine="true"
            android:textSize="17sp" />

        <Button
            android:id="@+id/title_bar_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="7dp"
            android:background="@null"
            android:minHeight="45dp"
            android:minWidth="45dp"
            android:textSize="14sp" />
    </RelativeLayout>
 
复制代码

然后在需要的地方通过include标签实现引用

复制代码
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lee="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/view_title_bar" />

</LinearLayout>
 
复制代码

通过上面的布局代码,我们可以使用上面这种方式确实实现了布局的复用,而且也避免了开发人员开发出参差不齐标题栏的问题,但是同时也引入了新的问题,比如更加降低了开发效率,加大了开发成本,问题就在我们该如何为每个布局文件定义标题栏?只有通过代码的方式来设置标题问题,左右按钮等其他的属性,导致布局属性和Activity代码耦合性比较高,所以这种方式也不是推荐的方式。

3.)第三种方式:通过自定义组合控件

    这里先不具体介绍如何实现一个自定义组合控件,这里先介绍一下自定义组合控件带来的好处。

  • 提高布局文件开发效率
  • 降低布局文件维护成本
  • 降低布局文件和Activity代码耦合性
  • 容易扩展
  • 简单易用

 如何实现一个自定义组合控件

1.)先定义一个布局文件
复制代码
 
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/title_bar_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="5dp"
        android:background="@null"
        android:minHeight="45dp"
        android:minWidth="45dp"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/title_bar_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:singleLine="true"
        android:textSize="17sp" />

    <Button
        android:id="@+id/title_bar_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="7dp"
        android:background="@null"
        android:minHeight="45dp"
        android:minWidth="45dp"
        android:textSize="14sp" />

</merge>
 
复制代码

注意:这里为何要使用merge标签,自定义组合控件时会继承RelativeLayout、LinearLayout等控件,这样导致布局的层级无形中增加了一层,如下是对比:

未使用merge标签

 

使用merge标签

2.)定义自定义属性

比如标题文字、标题栏左边按钮图标等。

复制代码
 
 <declare-styleable name="CustomTitleBar">
        <attr name="title_background_color" format="reference|integer" />
        <attr name="left_button_visible" format="boolean" />
        <attr name="right_button_visible" format="boolean" />
        <attr name="title_text" format="string" />
        <attr name="title_text_color" format="color" />
        <attr name="title_text_drawable" format="reference|integer" />
        <attr name="right_button_text" format="string" />
        <attr name="right_button_text_color" format="color" />
        <attr name="right_button_drawable" format="reference|integer" />
        <attr name="left_button_text" format="string" />
        <attr name="left_button_text_color" format="color" />
        <attr name="left_button_drawable" format="reference|integer" />
    </declare-styleable>
 
复制代码
3.)自定义一个View根据需求继承不同的ViewGroup子类,比如:RelativeLayout、LinearLayout等,我们这里继承RelativeLayout。
复制代码
 
public class CustomTitleBar extends RelativeLayout {
    private Button titleBarLeftBtn;
    private Button titleBarRightBtn;
    private TextView titleBarTitle;

    public CustomTitleBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.custom_title_bar, this, true);
        titleBarLeftBtn = (Button) findViewById(R.id.title_bar_left);
        titleBarRightBtn = (Button) findViewById(R.id.title_bar_right);
        titleBarTitle = (TextView) findViewById(R.id.title_bar_title);

        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CustomTitleBar);
        if (attributes != null) {
            //处理titleBar背景色
            int titleBarBackGround = attributes.getResourceId(R.styleable.CustomTitleBar_title_background_color, Color.GREEN);
            setBackgroundResource(titleBarBackGround);
            //先处理左边按钮
            //获取是否要显示左边按钮
            boolean leftButtonVisible = attributes.getBoolean(R.styleable.CustomTitleBar_left_button_visible, true);
            if (leftButtonVisible) {
                titleBarLeftBtn.setVisibility(View.VISIBLE);
            } else {
                titleBarLeftBtn.setVisibility(View.INVISIBLE);
            }
            //设置左边按钮的文字
            String leftButtonText = attributes.getString(R.styleable.CustomTitleBar_left_button_text);
            if (!TextUtils.isEmpty(leftButtonText)) {
                titleBarLeftBtn.setText(leftButtonText);
                //设置左边按钮文字颜色
                int leftButtonTextColor = attributes.getColor(R.styleable.CustomTitleBar_left_button_text_color, Color.WHITE);
                titleBarLeftBtn.setTextColor(leftButtonTextColor);
            } else {
                //设置左边图片icon 这里是二选一 要么只能是文字 要么只能是图片
                int leftButtonDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_left_button_drawable, R.mipmap.titlebar_back_icon);
                if (leftButtonDrawable != -1) {
                    titleBarLeftBtn.setBackgroundResource(leftButtonDrawable);
                }
            }

            //处理标题
            //先获取标题是否要显示图片icon
            int titleTextDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_title_text_drawable, -1);
            if (titleTextDrawable != -1) {
                titleBarTitle.setBackgroundResource(titleTextDrawable);
            } else {
                //如果不是图片标题 则获取文字标题
                String titleText = attributes.getString(R.styleable.CustomTitleBar_title_text);
                if (!TextUtils.isEmpty(titleText)) {
                    titleBarTitle.setText(titleText);
                }
                //获取标题显示颜色
                int titleTextColor = attributes.getColor(R.styleable.CustomTitleBar_title_text_color, Color.WHITE);
                titleBarTitle.setTextColor(titleTextColor);
            }

            //先处理右边按钮
            //获取是否要显示右边按钮
            boolean rightButtonVisible = attributes.getBoolean(R.styleable.CustomTitleBar_right_button_visible, true);
            if (rightButtonVisible) {
                titleBarRightBtn.setVisibility(View.VISIBLE);
            } else {
                titleBarRightBtn.setVisibility(View.INVISIBLE);
            }
            //设置右边按钮的文字
            String rightButtonText = attributes.getString(R.styleable.CustomTitleBar_right_button_text);
            if (!TextUtils.isEmpty(rightButtonText)) {
                titleBarRightBtn.setText(rightButtonText);
                //设置右边按钮文字颜色
                int rightButtonTextColor = attributes.getColor(R.styleable.CustomTitleBar_right_button_text_color, Color.WHITE);
                titleBarRightBtn.setTextColor(rightButtonTextColor);
            } else {
                //设置右边图片icon 这里是二选一 要么只能是文字 要么只能是图片
                int rightButtonDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_right_button_drawable, -1);
                if (rightButtonDrawable != -1) {
                    titleBarRightBtn.setBackgroundResource(rightButtonDrawable);
                }
            }
            attributes.recycle();
        }
    }

    public void setTitleClickListener(OnClickListener onClickListener) {
        if (onClickListener != null) {
            titleBarLeftBtn.setOnClickListener(onClickListener);
            titleBarRightBtn.setOnClickListener(onClickListener);
        }
    }

    public Button getTitleBarLeftBtn() {
        return titleBarLeftBtn;
    }

    public Button getTitleBarRightBtn() {
        return titleBarRightBtn;
    }

    public TextView getTitleBarTitle() {
        return titleBarTitle;
    }
}
 
复制代码
4.)在不同的XML布局中引用

关于如何使用自定义属性这里就不再说明了,为了更加直观的查看效果,我这里在一个布局文件中实现不同要求的标题栏

复制代码
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lee="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:right_button_drawable="@mipmap/titlebar_add_icon"
        lee:title_background_color="@color/green"
        lee:title_text="标题1" />

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:right_button_visible="false"
        lee:title_background_color="@color/green"
        lee:title_text="标题2" />

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:left_button_text="左边"
        lee:right_button_text="右边"
        lee:title_background_color="@color/red"
        lee:title_text="标题3" />

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:left_button_text="左边"
        lee:right_button_drawable="@mipmap/titlebar_add_icon"
        lee:title_background_color="@color/red"
        lee:title_text="标题4" />

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:left_button_text="左边"
        lee:left_button_text_color="@color/red"
        lee:right_button_drawable="@mipmap/titlebar_add_icon"
        lee:title_background_color="@color/blue"
        lee:title_text="标题5" />

    <com.whoislcj.views.CustomTitleBar
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginTop="10dp"
        lee:left_button_text="左边"
        lee:left_button_text_color="@color/red"
        lee:right_button_drawable="@mipmap/titlebar_add_icon"
        lee:title_background_color="@color/blue"
        lee:title_text="标题6"
        lee:title_text_color="@color/black" />

</LinearLayout>
 
复制代码

显示效果

总结:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
封面 1 序 2 捐助说明 5 目 录 7 第一章 View的绘图流程 12 1.1、概述 12 1.2、Activity的组成结构 13 1.3、View树的绘图流程 15 1.3.1 测量组件大小 16 1.3.2 确定子组件的位置 17 1.3.3 绘制组件 18 1.4、说点别的 22 1.5 练习作业 22 第二章 Graphics2D API 23 2.1、概述 23 2.2、Point类和PointF类 23 2.3、Rect类和RectF类 25 2.4、Bitmap类和BitmapDrawable类 32 2.5、Canvas类与Paint类 34 2.5.1 绘图概述 34 2.5.2 Paint类 34 2.5.3 Canvas类 39 2.6 练习作业 63 第章 使用Graphics2D实现动态效果 64 3.1 概述 64 3.2 invalidate()方法 65 3.3 坐标转换 69 3.4 剪切区(Clip) 73 3.5 案例:指针走动的手表 82 3.6 练习作业 88 第四章 双缓存技术 89 4.1 双缓存 89 4.2 在屏幕上绘制曲线 90 4.3 在屏幕上绘制矩形 99 4.4 案例:绘图App 104 4.4.1 绘图属性 106 4.4.2 软件参数 108 4.4.3 绘图缓冲区 109 4.4.4 撤消操作 111 4.4.5 图形绘制 113 4.4.6 绘图区 118 4.4.7 主界面 119 4.5 练习作业 122 第五章 阴影、渐变和位图运算 123 5.1 概述 123 5.2 阴影 123 5.3 渐变 125 5.3.1 线性渐变(LinearGradient) 126 5.3.2 径向渐变(RadialGradient) 130 5.3.3 扫描渐变(SweepGradient) 135 5.3.4 位图渐变(BitmapShader) 138 5.3.5 混合渐变(ComposeShader) 140 5.3.6 渐变与Matrix 142 5.4 位图运算 143 5.4.1 PorterDuffXfermode 143 5.4.2 图层(Layer) 146 5.4.3 位图运算技巧 148 5.5 案例1:圆形头像 152 5.6 案例2:刮刮乐 156 5.7 练习作业 161 第六章 自定义组件 163 6.1 概述 163 6.2 自定义组件的基本结构 164 6.3 重写onMeasure方法 166 6.4 组件属性 175 6.4.1 属性的基本定义 175 6.4.2 读取来自style和theme中的属性 181 6.5 案例1:圆形ImageView组件 186 6.6 案例2:验证码组件CodeView 190 6.7 练习作业 202 第七章 自定义容器 204 7.1 概述 204 7.2 ViewGroup类 205 7.2.1 ViewGroup常用方法 205 7.2.2 ViewGroup的工作原理 208 7.2.3 重写onLayout()方法 213 7.3 CornerLayout布局 217 7.3.1 基本实现 217 7.3.2 内边距padding 224 7.3.3 外边距margin 228 7.3.4 自定义LayoutParams 238 7.4 案例:流式布局(FlowLayout) 246 7.5 练习作业 256 第八章 Scroller与平滑滚动 257 8.1 概述 257 8.2 认识scrollTo()和scrollBy()方法 258 8.3 Scroller类 264 8.4 平滑滚动的工作原理 271 8.5 案例:触摸滑屏 272 8.5.1 触摸滑屏的技术分析 272 8.5.2 速度跟踪器VelocityTracker 273 8.5.3 触摸滑屏的分步实现 274 8.6 练习作业 285 第九章 侧边栏 287 9.1 概述 287 9.2 使用二进制保存标识数据 289 9.2.1 位运算符 289 9.2.2 位运算的常用功能 292 9.3 继承自ViewGroup的侧边栏 293 9.4 继承自HorizontalScrollView的侧边栏 304 9.5 练习作业 312 第十章 加强版ListView 313 10.1 概述 313 10.2 ListView的基本使用 314 10.3 ListItem随手指左右滑动 318 10.4 向右滑动删除ListItem 326 10.5 滑动ListItem出现删除按钮 336 10.5.1 列表项专用容器ExtendLayout 337 10.5.2 列表项能滑出删除按钮的ListView 342 10.5.3 定义布局文件 350 10.5.4 显示ListView 351 10.6练习作业 353 案例代码说明 354

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值