前言
第一次写博客有些紧张,本人是一名大三学生,水平有限,如有错误请指证交流,也欢迎各式的话题讨论。
这是一篇关于自定义控件之组合控件的文章,主要实现了类似主流app上常见的横状栏(菜单列表)的简单组合封装。
贴一个目前项目中正在使用的效果:
最终实现了:左图标、左文字、右文字、右图标、右箭头的内容定制,以及下划线、下划区域的定制。
需求:
因为目前的项目,本人负责个人中心的开发,所以难免要用到大量这样的横状栏去表示功能入口。但如果针对每一个横状栏都要重复地去写布局,就会使代码重复性、耦合度太高,也不便于修改。
于是想到自定义控件的方式来实现如上效果。
思路:
重温自定义控件的三种方式:
1.继承View:通过重写ondraw方法等,以绘图的方式自定义View。
2.继承ViewGroup:继承ViewGroup、LinearLayout、FrameLayout等。
3.组合控件:对已有控件进行组合封装,设置属性接口。
本文主要是基于组合控件。
实践:
1.布局编写
基本思路就是编写一个横状栏的布局文件,我们再在后面通过属性接口使用户可以通过属性的设置来选择哪些控件展示、展示的控件展示什么内容。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/padding_8"
>
<ImageView
android:id="@+id/left_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_8"/>
<TextView
android:id="@+id/left_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:layout_marginLeft="@dimen/padding_8"
android:textColor="@color/black"
android:textSize="@dimen/text_size_normal_14" />
<TextView
android:id="@+id/right_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_10"
android:textColor="@color/hint_grey"
android:textSize="@dimen/text_size_normal_14"
/>
<ImageView
android:id="@+id/right_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_8"
/>
<ImageView
android:id="@+id/right_arrow"
android:layout_width="25dp"
android:layout_height="30dp"
android:layout_marginLeft="0dp"
android:src="@drawable/ic_personal_info_arrow"
/>
</LinearLayout>
<View
android:id="@+id/divide_line_view"
android:layout_width="match_parent"
android:layout_height="@dimen/view_size_1dp"
android:background="@color/background_grey"
android:visibility="gone" />
<View
android:id="@+id/divide_area_view"
android:layout_width="match_parent"
android:layout_height="@dimen/height_8"
android:background="@color/background_grey"
android:visibility="gone" />
</LinearLayout>
2.资源文件的编写
这里有关dimen尺寸定义以及图标资源文件就不列出了。
2.1色彩定义in colors.xml:
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
<color name="hint_grey">#b7b7b7</color>
<color name="background_grey">#F6F6F6</color>
2.2属性定义in attrs.xml:
一般属性的类型有下面这九种:
foromat | 解释 |
---|---|
color | 颜色值 |
boolean | 布尔值 |
dimesion | 尺寸值 |
float | 浮点值 |
integer | 整型值 |
string | 字符串 |
fraction | 百分数 |
enum | 枚举值 |
reference | 引用资源文件 |
在<declare-styleable name="ItemHorizontal"> 中给自己定义的横状栏起一个名字,这个名字用来在自定义控件中获取相关属性。
<attr name="left_text" format="string"/> 自定义所需要的属性名称,以及这个属性的类型。
<declare-styleable name="ItemHorizontal">
<attr name="left_text" format="string" />
<attr name="left_icon" format="reference" />
<attr name="right_text" format="string" />
<attr name="right_icon" format="reference" />
<attr name="is_show_right_icon" format="boolean" />
<attr name="is_show_left_icon" format="boolean" />
<attr name="is_show_right_arrow" format="boolean" />
<attr name="divide_line_style" format="integer" />
</declare-styleable>
注:styleable标签可以不写。去除styleable标签后在第3步的typedArray获取属性时传入对应常量即可。
3.继承ViewGroup子类
实际上这就是我们前面提到的三种自定义控件方式中的第二种。
package com.example.personalcenter.ui.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.personalcenter.R;
public class ItemHorizontal extends FrameLayout {
private Context mContext;
private View mView;
private TextView leftTv;
private TextView rightTv;
private ImageView leftImageView;
private ImageView rightImageView;
private ImageView rightArrow;
public static final int NO_LINE = 0;
public static final int DIVIDE_LINE = 1;
public static final int DIVIDE_AREA = 2;
private String leftText;
private String rightText;
boolean isShowLeftIcon;
boolean isShowRightIcon;
boolean isShowRightArrow;
private int divideLineStyle = NO_LINE;
public String getLeftText() {
return leftText;
}
public void setLeftImageView(boolean isLeftIconShow, int iconImgId) {
if (isLeftIconShow) {
if (iconImgId != 9102) {
leftImageView.setImageResource(iconImgId);
}
} else {
leftImageView.setVisibility(GONE);
}
}
public void setRightImageView(boolean isRightIconShow, int iconImgId) {
if (isRightIconShow) {
if (iconImgId != 9102) {
rightImageView.setImageResource(iconImgId);
}
} else {
rightImageView.setVisibility(GONE);
}
}
public void setRightArrowView(boolean isRightArrowShow) {
if (!isRightArrowShow) {
rightArrow.setVisibility(INVISIBLE);
}
}
public void setLeftText(String leftText) {
if (leftText != null) {
this.leftText = leftText;
leftTv.setText(leftText);
}
}
public String getRightText() {
return rightText;
}
public void setRightText(String rightText) {
if (rightText != null) {
this.rightText = rightText;
rightTv.setText(rightText);
}
}
public void setDivideLineStyle(int divideLineLineStyle) {
View lineView = findViewById(R.id.divide_line_view);
View areaView = findViewById(R.id.divide_area_view);
if(divideLineLineStyle==DIVIDE_LINE){
lineView.setVisibility(VISIBLE);
}
else if (divideLineLineStyle == DIVIDE_AREA ) {
areaView.setVisibility(VISIBLE);
}
}
public ItemHorizontal(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
}
public void init(Context context, AttributeSet attributeSet) {
mContext = context;
LayoutInflater.from(context).inflate(R.layout.item_horizontal, this);
leftTv = (TextView) findViewById(R.id.left_text);
rightTv = (TextView) findViewById(R.id.right_text);
leftImageView = (ImageView) findViewById(R.id.left_icon);
rightImageView = (ImageView) findViewById(R.id.right_icon);
rightArrow=(ImageView)findViewById(R.id.right_arrow);
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ItemHorizontal);
isShowLeftIcon = typedArray.getBoolean(R.styleable.ItemHorizontal_is_show_left_icon, false);
isShowRightIcon = typedArray.getBoolean(R.styleable.ItemHorizontal_is_show_right_icon, false);
isShowRightArrow= typedArray.getBoolean(R.styleable.ItemHorizontal_is_show_right_arrow, true);
divideLineStyle = typedArray.getInt(R.styleable.ItemHorizontal_divide_line_style, NO_LINE);
setLeftImageView(isShowLeftIcon, typedArray.getResourceId(R.styleable.ItemHorizontal_left_icon, 9102));
setRightImageView(isShowRightIcon,typedArray.getResourceId(R.styleable.ItemHorizontal_right_icon, 9102));
setRightArrowView(isShowRightArrow);
setLeftText(typedArray.getString(R.styleable.ItemHorizontal_left_text));
setRightText(typedArray.getString(R.styleable.ItemHorizontal_right_text));
setDivideLineStyle(divideLineStyle);
typedArray.recycle();//注意回收
}
}
最终使用
<com.example.personalcenter.ui.widget.ItemHorizontal
android:id="@+id/ih_settings_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:right_text="@string/settings_account_safe"
app:is_show_right_icon="true"
app:right_icon="@drawable/ic_settings_safe"
app:left_text="@string/settings_account"
app:divide_line_style="1"/>
这样就可以达到最开始那张图的效果啦,具体的样式变化通过属性设置就可以达到定制的效果。
至此,一个基本的自定义组合控件流程就宣告完成。
拓展:
但在这个流程中,相信还可能会出现不少疑问:
1.AttributeSet与TypedArray间的联系?
2.typedeArray是什么?为什么要使用TypedArray
如果理解了这些,那相信以后再遇到自定义组合控件的开发任务就会驾轻就熟啦。
这些内容在鸿洋大神的这篇博客中已经讲的很清楚,这里就不再赘述了。想要学习的朋友点击下方链接即可。
[鸿洋大神的博客]https://blog.csdn.net/lmj623565791/article/details/45022631/
后记
唔,一直有写博客的想法,但是始终因为各种各样的原因而一再推迟。终于,在这个安静的夜晚,我决定使用简书来记录我的日常感悟与开发心得。
不知怎得,突然就想起《邪不压正》里这两人的对话:“正经人谁写日记啊?”,“你写日记吗?”