前言
前段时间封装过一个控件,效果如下图所示:
使用者只需传入button的数据即可实现效果。因为按钮的样式都是用xml文件实现的,因此颜色也是指定好的。后来想着别人要使用你的控件,颜色肯定需要和他们自己的应用搭配,所以如果要做好就需要提供一个属性,可以让使用者指定控件的主要颜色。可是我不知道要如何在代码中动态改变drawable文件中指定的颜色,那时候还问过一位技术人员,是否可以在代码里动态改变xml文件中的颜色,那时他说不行,要多个颜色就写多个xml文件,但我想着应该是可以的吧,要不然别人封装的控件就只能有一个颜色吗?后来果然查到有一种方法可以实现效果,可见还是要自己实践一下才知道行不行,因此记录一下。
参考方法地址:http://www.jianshu.com/p/5353745f07db
正文
为了定义一个自定义控件的属性,我在values文件夹下建立一个attrs.xml
并在自定义View的构造方法中获取该属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TabButtonRecyclerView, defStyle, 0);
// 默认color.xml中定义的mainColor
mainColor = typedArray.getColor(R.styleable.TabButtonRecyclerView_mainColor, getResources().getColor(R.color.mainColor));
因为不是每一个按钮的样式都是一样的,比如说两边的按钮是有圆角的,因此我没有用selector选择器来实现点击切换样式的效果,而是在代码里控制切换,故关键代码就写在切换效果的地方。
比如其中一个drawable下的xml文件如下,是一个shape元素:
所以修改颜色的代码如下:
holder.tvButtonText.setBackgroundResource(R.drawable.shape_left_select);
// shape_left_select.xml里是一个shape元素,设置该元素的颜色为用户指定的颜色
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setColor(mainColor);
如果修改的是边框的颜色,则需要写drawable.setStroke(2, mainColor);
其他都是用类似的方式改变颜色,但其中遇到了一个特殊的情况,比如说xml文件不是一个shape元素,而是一个layer-list元素,上面的两行代码就会报错了
例如一个xml文件如下:
-
-
那么就需要以下的代码进行修改了:
holder.tvButtonText.setBackgroundResource(R.drawable.shape_middle);
// 此处使用的是LayerDrawable,是因为shape_middle.xml内定义的是一个layer-list元素
// layer-list是将多个图片或shape、selector等种效果按照顺序层叠起来,每一个是用item包裹起来
// 而shape_middle.xml包含一个边框shape和一个内容shape
// 又因是未选择状态,内容为白色,边框为主色调,故先取出第一个元素(表示边框的shape),设置颜色为主色调
LayerDrawable layerDrawable = (LayerDrawable) holder.tvButtonText.getBackground();
GradientDrawable drawable = (GradientDrawable)layerDrawable.getDrawable(0);
drawable.setColor(mainColor);
这样在使用的时候指定颜色属性就可以实现属于自己的控件效果了,要使用自定义属性还需要加上一句:
xmlns:app=“http://schemas.android.com/apk/res-auto”
----------------------------------------时隔一年的分割线-------------------------------
这个当时的记录文档,前些日子在项目中用到过这个控件,发现还是有很多可以优化的地方,等有时间优化了再更新,目前至少效果可以实现的~~
自定义控件TabButtonView
一、最终效果
二、实现思路
1.该控件的button的个数是动态的,并且是横向排列,因此选择使用RecyclerView
2.需要封装按钮的点击效果,比较特殊的是,左右两边的按钮有一半是圆角,而中间的按钮是没有圆角,这点需要进行判断
3.该自定义控件需要提供一个属性给用户指定button的颜色
三、目录结构
1.代码类:
a)TabButtonItemBean.java:按钮bean类
b)TabButtonRecyclerAdapter.java:按钮适配器
c)TabButtonRecyclerView.java:自定义View类
2.布局文件:
item_tab_button.xml:单个button的样式布局
3.资源文件:
a)shape_left.xml:最左边的按钮(左边圆角)未选中样式
b)shape_left_select.xml:最左边的按钮(左边圆角)选中样式
c)shape_middle.xml:中间的按钮(没有圆角)未选中样式
d)shape_middle_select.xml:中间的按钮(没有圆角)选中样式
e)shape_right.xml:最右边的按钮(右边圆角)未选中样式
f)shape_right_select.xml:最右边的按钮(右边圆角)选中样式
4.属性文件:
a)attrs.xml:定义自定义控件的属性(指定颜色)
b)colors.xml:定义自定义控件的颜色
四、代码
1.配置文件build.gradle
要想使用RecyclerView,需要在build.gradle中加入依赖
compile 'com.android.support:recyclerview-v7:26.1.0'
注意:recyclerview的版本需要和appcompat的版本一致
2.View
TabButtonRecyclerView.java
public class TabButtonRecyclerView extends RecyclerView {
private Context context;
private OnItemViewClickListener listener;
/**
* Tab Button 的颜色
*/
private int mainColor;
public TabButtonRecyclerView(Context context) {
super(context);
this.context = context;
// 为了是使该控件在布局文件内就有横排展示的预览效果
LayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
this.setLayoutManager(layoutManager);
}
public TabButtonRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public TabButtonRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
// TypedArray是存储资源数组的容器,它可以通过obtainStyledAttributes()创建出来
// 不过创建完了,如果不再使用了,请注意调用recycle()把它释放掉
// 通过obtainStyledAttributes获得一组值赋给TypedArray(数组),这一组值来自于res/values/attrs.xml
// 中的name="TabButtonRecyclerView"的declare-styleable中
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TabButtonRecyclerView, defStyle, 0);
// 默认color.xml中定义的mainColor
mainColor = typedArray.getColor(R.styleable.TabButtonRecyclerView_mainColor, getResources().getColor(R.color.mainColor));
// 为了是使该控件在布局文件内就有横排展示的预览效果
LayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
this.setLayoutManager(layoutManager);
}
public void setOnItemViewClickListener(OnItemViewClickListener listener) {
this.listener = listener;
}
/**
* 对外接口
* @param list button内容
*/
public void setData(List list) {
TabButtonRecyclerAdapter adapter = new TabButtonRecyclerAdapter(list, context, mainColor);
this.setAdapter(adapter);
adapter.setOnItemClickListener(new TabButtonRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position, TabButtonItemBean data) {
listener.onItemViewClick(position, data);
}
});
}
public interface OnItemViewClickListener {
void onItemViewClick(int position, TabButtonItemBean data);
}
}
3.Bean
TabButtonBean.java
public class TabButtonItemBean {
private int code;
private String text;
//getter/setter略…
}
4.布局文件
item_tab_button.xml
5.Adapter
TabButtonRecyclerAdapter.java
public class TabButtonRecyclerAdapter extends RecyclerView.Adapter {
private List data;
private Context context;
private OnItemClickListener mListener;
private List viewHolders = new ArrayList<>();
/**
* Tab Button 的颜色
*/
private int mainColor;
public TabButtonRecyclerAdapter(List data, Context context, int mainColor) {
this.data = data;
this.context = context;
this.mainColor = mainColor;
}
public void setOnItemClickListener(OnItemClickListener mListener) {
this.mListener = mListener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 实例化展示的view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tab_button, parent, false);
// 实例化ViewHolder
ViewHolder viewHolder = new ViewHolder(view);
viewHolders.add(viewHolder);
return viewHolder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
// 绑定数据
holder.tvButtonText.setText(data.get(position).getText());
if (position == 0){
// 第一个button,左边圆角,默认选中状态
holder.tvButtonText.setTextColor(context.getResources().getColor(R.color.white));
holder.tvButtonText.setBackgroundResource(R.drawable.shape_left_select);
// shape_left_select.xml里是一个shape元素,设置该元素的颜色为用户指定的颜色
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setColor(mainColor);
} else if (position == data.size() - 1){
// 最后一个button,右边圆角,默认未选中状态
holder.tvButtonText.setTextColor(mainColor);
holder.tvButtonText.setBackgroundResource(R.drawable.shape_right);
// shape_right.xml里有是一个shape元素,关于颜色定义了两个元素,一个是solid,另一个是stroke
// 因为是未选择状态,背景为白色,边框为主色调,因此设置stroke的颜色为用户指定的颜色
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setStroke(2, mainColor);
} else {
// 中间button,没有圆角,默认未选中状态
holder.tvButtonText.setTextColor(mainColor);
holder.tvButtonText.setBackgroundResource(R.drawable.shape_middle);
// 此处使用的是LayerDrawable,是因为shape_middle.xml内定义的是一个layer-list元素
// layer-list是将多个图片或shape、selector等种效果按照顺序层叠起来,每一个是用item包裹起来
// 而shape_middle.xml包含一个边框shape和一个内容shape
// 又因是未选择状态,内容为白色,边框为主色调,故先取出第一个元素(表示边框的shape),设置颜色为主色调
LayerDrawable layerDrawable = (LayerDrawable) holder.tvButtonText.getBackground();
GradientDrawable drawable = (GradientDrawable)layerDrawable.getDrawable(0);
drawable.setColor(mainColor);
}
if (mListener != null) {
holder.tvButtonText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
refreshAllViews();
holder.tvButtonText.setTextColor(context.getResources().getColor(R.color.white));
// 设置选中状态
if (position == 0) {
// 第一个button,左边圆角
holder.tvButtonText.setBackgroundResource(R.drawable.shape_left_select);
// 设置按钮背景为用户使用时是定的主色调
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setColor(mainColor);
} else if (position == data.size() - 1) {
holder.tvButtonText.setBackgroundResource(R.drawable.shape_right_select);
// 设置按钮背景为用户使用时是定的主色调
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setColor(mainColor);
} else {
holder.tvButtonText.setBackgroundResource(R.drawable.shape_middle_select);
// shape_middle_select.xml内是一个layer-list元素
LayerDrawable layerDrawable = (LayerDrawable) holder.tvButtonText.getBackground();
// 中间按钮的上下边框和内容在选中状态下颜色都是主色调
GradientDrawable drawable1 = (GradientDrawable) layerDrawable.getDrawable(0);
drawable1.setColor(mainColor);
GradientDrawable drawable2 = (GradientDrawable) layerDrawable.getDrawable(1);
drawable2.setColor(mainColor);
}
mListener.onItemClick(v, position, data.get(position));
}
});
}
}
@Override
public int getItemCount() {
return data == null ? 0 : data.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvButtonText;
public ViewHolder(View itemView) {
super(itemView);
tvButtonText = itemView.findViewById(R.id.tv_button_text);
}
}
public interface OnItemClickListener{
void onItemClick(View view, int position, TabButtonItemBean data);
}
/**
* 设置所有的button都为未选择状态
*/
public void refreshAllViews() {
ViewHolder view;
for (int i = 0; i < viewHolders.size(); i++) {
view = viewHolders.get(i);
view.tvButtonText.setTextColor(mainColor);
if (i == 0) {
// 第一个button,左边圆角
view.tvButtonText.setBackgroundResource(R.drawable.shape_left);
LayerDrawable layerDrawable = (LayerDrawable) view.tvButtonText.getBackground();
GradientDrawable drawable = (GradientDrawable)layerDrawable.getDrawable(0);
// 未选中状态下都是内容为白色,边框为主色调
drawable.setStroke(2, mainColor);
} else if (i == viewHolders.size() - 1) {
// 最后一个button,右边圆角
view.tvButtonText.setBackgroundResource(R.drawable.shape_right);
GradientDrawable drawable =(GradientDrawable)view.tvButtonText.getBackground();
// 未选中状态下都是内容为白色,边框为主色调
drawable.setStroke(2, mainColor);
} else {
// 中间button,没有圆角
view.tvButtonText.setBackgroundResource(R.drawable.shape_middle);
// shape_middle.xml没是一个layer-list元素,因此复杂些,第一个元素表示边框shape
LayerDrawable layerDrawable = (LayerDrawable) view.tvButtonText.getBackground();
GradientDrawable drawable = (GradientDrawable)layerDrawable.getDrawable(0);
drawable.setColor(mainColor);
}
}
}
}
因为button的背景颜色是用drawable文件来定义的,因此当用户指定了颜色时,需要改变drawable中指定的颜色,关键代码是:
GradientDrawable drawable =(GradientDrawable)holder.tvButtonText.getBackground();
drawable.setColor(mainColor);
6.资源文件drawable:
1)shape_left.xml(左边button未选中样式)
-
-
2)shape_left_select.xml(左边button选中样式)
3)shape_middle.xml(中间button未选中样式)
-
-
4)shape_middle_select.xml(中间button选中样式)
-
-
5)shape_right.xml(右边button未选中样式)
6)shape_right_select.xml(右边button选中样式)
7.资源文件
1)colors.xml
#ffffff
#48AA9B
2)attrs.xml
五、使用
1)布局文件
2)Java文件
TabButtonRecyclerView tabButtonRecyclerView = findViewById(R.id.tab_button);
tabButtonRecyclerView.setData(buttons);
tabButtonRecyclerView.setOnItemViewClickListener(new TabButtonRecyclerView.OnItemViewClickListener() {
@Override
public void onItemViewClick(int position, TabButtonItemBean data) {
Toast.makeText(activity, data.getText(), Toast.LENGTH_SHORT).show();
}
});
public List initData2(){
List list = new ArrayList<>();
TabButtonItemBean bean1 = new TabButtonItemBean();
bean1.setCode(0);
bean1.setText("待执行");
list.add(bean1);
TabButtonItemBean bean2 = new TabButtonItemBean();
bean2.setCode(1);
bean2.setText("进行中");
list.add(bean2);
TabButtonItemBean bean4 = new TabButtonItemBean();
bean4.setCode(3);
bean4.setText("已完成");
list.add(bean4);
return list;
}
}