美文网首页
关于带布局的自定义View(自定义TitleView)

关于带布局的自定义View(自定义TitleView)

作者: 奋斗的bidHead | 来源:发表于2019-03-20 09:45 被阅读0次

    问题:多一层外套的组合自定义View

    当自定义View需要引入R.layout.view_xxx时,在R.layout.view_xxx尽量使用merge标签,这样可以减少一层ViewGroup的嵌套。

    引用一个网上看到的例子:

    组合自定义View在日常开发中的引用场景应该还是相对比较多的,下面我粘上一个简单的组合自定义View的实现:

    /**
     * 简单的一个组合自定义View,在它的中央会显示一个TextView文本
     * 此案例的布局填充方式也是目前网上包括一些书籍描述的填充方式
     */
    public class GroupNormalView extends RelativeLayout {
        
        public GroupNormalView(Context context) {
            this(context, null);
        }
    
        public GroupNormalView(Context context, AttributeSet attrs) {
            super(context, attrs);
            LayoutInflater.from(context).inflate(R.layout.group_normal_view, this, true);
        }
    }
    

    xml布局:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="25sp"
            android:textColor="#ff0000"
            android:text="Normal" />
    </RelativeLayout>
    

    然后,我在MainActivity的布局中引入这个自定义View。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="activity.MainActivity">
    
        <view.GroupNormalView
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </RelativeLayout>
    

    运行demo,当页面打开后,我们使用AS自带的Layout Inspector工具来查看当前页面的布局组成。如下图:


    在这里插入图片描述

    Layout Inspector工具的位置


    在这里插入图片描述
    当前页面的布局内容中仅含一个GroupNormalView,在它的里面包含一层相对布局,然后还有一个TextView。看最初定义GroupNormalView的时候我直接让它继承了RelativeLayout,也就是说GroupNormalView本身就含有RelativeLayout的全部布局属性了,那么现在有没有觉着上图中第二个箭头所指的RelativeLayout有些多余呢?事实证明,真的很多余。下面就让我们把这多余的一层布局给干掉。

    解决方式

    取个简单的例子:
    当我们自己封装TitleBar的时候往往会引入一个R.layout.layout_title布局。
    R.layout.layout_title:

    <?xml version="1.0" encoding="utf-8"?>
    <merge 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="wrap_content"
        xmlns:tools="http://schemas.android.com/tools"
        tools:parentTag="android.support.constraint.ConstraintLayout">
    
    
        <View
            android:id="@+id/v_bg"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:background="@color/white"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <TextView
            android:id="@+id/title_bar_back"
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:drawableStart="@mipmap/title_bar_back"
            android:gravity="center_vertical"
            android:paddingStart="15dp"
            android:paddingEnd="10dp"
            app:layout_constraintBottom_toBottomOf="@id/v_bg"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@id/v_bg" />
    
        <TextView
            android:id="@+id/title_bar_close"
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:drawableStart="@mipmap/title_bar_close"
            android:gravity="center_vertical"
            android:paddingStart="10dp"
            android:paddingEnd="10dp"
            app:layout_constraintBottom_toBottomOf="@id/v_bg"
            app:layout_constraintStart_toEndOf="@id/title_bar_back"
            app:layout_constraintTop_toTopOf="@id/v_bg" />
    
        <TextView
            android:id="@+id/title_bar_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="1"
            android:text="Title"
            android:textColor="@color/black"
            android:textSize="@dimen/title_size"
            app:layout_constraintBottom_toBottomOf="@id/v_bg"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@id/v_bg"
    
            />
    
        <TextView
            android:id="@+id/title_bar_right"
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:gravity="center_vertical"
            android:paddingStart="10dp"
            android:paddingEnd="15dp"
            android:textColor="@color/black"
            android:text="right"
            android:textSize="@dimen/title_right_size"
            app:layout_constraintBottom_toBottomOf="@id/v_bg"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@id/v_bg" />
    
    
        <View
            android:id="@+id/v_divider_line"
            android:layout_width="0dp"
            android:layout_height="4dp"
            android:background="@drawable/shape_title_shadow"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/v_bg" />
    </merge>
    

    tips:这里我们根布局选用ConstraintLayout布局,铺布局时可以先放一个ConstraintLayout当做根布局。在布局完成后把ConstraintLayout替换成merge标签,在merge中加入如下代码来让preview正常显示布局样式。

    xmlns:tools="http://schemas.android.com/tools"
    tools:parentTag="android.support.constraint.ConstraintLayout"
    

    如图:


    在这里插入图片描述

    下面是TitleView:

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.drawable.Drawable;
    import android.support.constraint.ConstraintLayout;
    import android.support.v4.content.ContextCompat;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.TextView;
    
    import com.base.lib.R;
    
    public class TitleView extends ConstraintLayout {
        private TextView tvBack;
        private TextView tvRight;
        private TextView tvTitle;
        private TextView tvClose;
        //背景颜色
        private View v_bg;
        //分割栏
        private View barLine;
    
        private String titleText;
        private int titleTextColor;
    
        private Drawable backIcon;
    
        private String rightText;
        private int rightTextColor;
    
        private int backColor;
        private boolean dividerHide;
        private boolean backHide;
        private boolean closeHide;
    
    
        public TitleView(Context context) {
            this(context, null);
        }
    
        public TitleView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public TitleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(attrs);
        }
    
        private void initAttrs(AttributeSet attrs) {
    
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.TitleView);
            titleText = typedArray.getString(R.styleable.TitleView_titleText);
            titleTextColor = typedArray.getColor(R.styleable.TitleView_titleColor, ContextCompat.getColor(getContext(), R.color.black));
    
            backColor = typedArray.getColor(R.styleable.TitleView_backColor, ContextCompat.getColor(getContext(), R.color.white));
    
            rightText = typedArray.getString(R.styleable.TitleView_rightText);
            rightTextColor = typedArray.getColor(R.styleable.TitleView_rightTextColor, ContextCompat.getColor(getContext(), R.color.black));
    
            backIcon = typedArray.getDrawable(R.styleable.TitleView_backIcon);
            dividerHide = typedArray.getBoolean(R.styleable.TitleView_dividerHide, false);
            backHide = typedArray.getBoolean(R.styleable.TitleView_backHide, false);
            closeHide = typedArray.getBoolean(R.styleable.TitleView_closeHide, false);
    
            typedArray.recycle();
    
    
        }
    
        /**
         * 初始化文字标题栏
         */
        private void initView(AttributeSet attrs) {
    
            initAttrs(attrs);
            View view = View.inflate(getContext(), R.layout.layout_title, this);
    
            v_bg = view.findViewById(R.id.v_bg);
    
            tvTitle = view.findViewById(R.id.title_bar_name);
            tvBack = view.findViewById(R.id.title_bar_back);
            tvRight = view.findViewById(R.id.title_bar_right);
            tvClose = view.findViewById(R.id.title_bar_close);
    
            barLine = view.findViewById(R.id.v_divider_line);
    
            setTitleColor(titleTextColor);
            setTitleText(titleText);
            setBackgroundColor(backColor);
            setBarLineHide(dividerHide);
            setRightColor(rightTextColor);
            if (null == backIcon) {
                backIcon = ContextCompat.getDrawable(getContext(), R.mipmap.title_bar_back);
                setBackDrawable(backIcon);
            } else {
                setBackDrawable(backIcon);
            }
            setBackHide(backHide);
            setCloseHide(closeHide);
    
            if (!TextUtils.isEmpty(rightText)) {
                setRightText(rightText);
            }
    
        }
    
    
        /**
         * 设置背景颜色
         *
         * @param color
         */
        public void setBackgroundColor(int color) {
            this.backColor = color;
            if (null != v_bg) {
                v_bg.setBackgroundColor(color);
            }
        }
    
    
        /**
         * 设置标题
         *
         * @param title
         */
        public void setTitleText(String title) {
            this.titleText = title;
            if (null != tvTitle) {
                tvTitle.setText(title);
            }
            ;
        }
    
        /**
         * 设置标题
         */
        public String getTitleText() {
            return titleText;
        }
    
        /**
         * 设置标题字体颜色
         *
         * @param color
         */
        public void setTitleColor(int color) {
            this.titleTextColor = color;
            tvTitle.setTextColor(color);
        }
    
    
        /**
         * 分割线隐藏
         *
         * @param isHide
         */
        public void setBarLineHide(boolean isHide) {
            this.dividerHide = isHide;
    
            if (isHide) {
                if (barLine.getVisibility() == View.VISIBLE) {
                    barLine.setVisibility(View.GONE);
                }
            } else {
                if (barLine.getVisibility() == View.GONE) {
                    barLine.setVisibility(View.VISIBLE);
                }
            }
    
        }
    
        /**
         * 返回键隐藏
         *
         * @param isHide
         */
        public void setBackHide(boolean isHide) {
            this.backHide = isHide;
            if (null != tvBack) {
                if (isHide) {
                    if (tvBack.getVisibility() == View.VISIBLE) {
                        tvBack.setVisibility(View.GONE);
                    }
                } else {
                    if (tvBack.getVisibility() == View.GONE) {
                        tvBack.setVisibility(View.VISIBLE);
                    }
                }
            }
        }
    
        /**
         * 关闭键隐藏
         *
         * @param isHide
         */
        public void setCloseHide(boolean isHide) {
            this.closeHide = isHide;
            if (null != tvClose) {
                if (isHide) {
                    if (tvClose.getVisibility() == View.VISIBLE) {
                        tvClose.setVisibility(View.GONE);
                    }
                } else {
                    if (tvClose.getVisibility() == View.GONE) {
                        tvClose.setVisibility(View.VISIBLE);
                    }
                }
            }
        }
    
        /**
         * 设置返回键图标
         *
         * @param drawable
         */
        public void setBackDrawable(Drawable drawable) {
            this.backIcon = drawable;
            if (tvBack != null) {
                tvBack.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
            }
        }
    
        /**
         * 设置返回键监听事件
         */
        public void setOnBackListener(View.OnClickListener clickListener) {
    
            if (null != tvBack) {
                if (clickListener != null) {
                    tvBack.setOnClickListener(clickListener);
                }
            }
        }
    
        /**
         * 设置右边功能颜色
         */
        public void setRightColor(int color) {
            this.rightTextColor = color;
            if (null != tvRight) {
                tvRight.setTextColor(color);
            }
        }
    
        /**
         * 设置右边标题文字
         */
        public void setRightText(String title) {
            this.rightText = title;
            if (null != tvRight) {
                tvRight.setText(title);
            }
        }
    
        /**
         * 设置右边功能监听事件
         */
        public void setOnRightListener(View.OnClickListener clickListener) {
            if (null != tvRight) {
                if (clickListener != null) {
                    tvRight.setOnClickListener(clickListener);
                }
            }
    
        }
    }
    

    TitleView继承自ConstraintLayout,加载布局时因为merge的关系 View view = View.inflate(getContext(), R.layout.layout_title, this);这个this一定不能为空。

    再贴上attr代码:

    <declare-styleable name="TitleView">
            <attr name="titleText" format="string|reference" />
            <attr name="titleColor" format="color|reference" />
            <attr name="backColor" format="color|reference" />
            <attr name="rightText" format="string|reference" />
            <attr name="rightTextColor" format="color|reference" />
            <attr name="rightTextSize" format="dimension|reference" />
            <attr name="backIcon" format="color|reference" />
            <attr name="dividerHide" format="boolean" />
            <attr name="backHide" format="boolean" />
            <attr name="closeHide" format="boolean" />
    </declare-styleable>
    

    drawable代码:

    <?xml version="1.0" encoding="utf-8"?>
    <!--适配5.0版本以下的阴影-->
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <gradient
            android:angle="90"
            android:endColor="#0f000000"
            android:startColor="@android:color/transparent" />
    </shape>
    

    布局检测:


    在这里插入图片描述

    可以清晰的看到TitleView中没有额外的父布局。

    源码

    https://github.com/alinainai/Base/blob/master/commonlib/src/main/java/com/base/lib/view/TitleView.java

    参考:

    https://www.jianshu.com/p/3c6e763d48fc?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends
    https://www.jianshu.com/p/69e1a3743960

    相关文章

      网友评论

          本文标题:关于带布局的自定义View(自定义TitleView)

          本文链接:https://www.haomeiwen.com/subject/vxiamqtx.html