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

问题:多一层外套的组合自定义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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值