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