Android自定义控件提供颜色属性动态改变控件颜色

前言
前段时间封装过一个控件,效果如下图所示:
Android自定义控件提供颜色属性动态改变控件颜色_第1张图片
使用者只需传入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”




效果
Android自定义控件提供颜色属性动态改变控件颜色_第2张图片
如有错误,望大神指出,非常感谢!

----------------------------------------时隔一年的分割线-------------------------------

这个当时的记录文档,前些日子在项目中用到过这个控件,发现还是有很多可以优化的地方,等有时间优化了再更新,目前至少效果可以实现的~~

自定义控件TabButtonView

一、最终效果
Android自定义控件提供颜色属性动态改变控件颜色_第3张图片
二、实现思路
1.该控件的button的个数是动态的,并且是横向排列,因此选择使用RecyclerView
2.需要封装按钮的点击效果,比较特殊的是,左右两边的按钮有一半是圆角,而中间的按钮是没有圆角,这点需要进行判断
3.该自定义控件需要提供一个属性给用户指定button的颜色
三、目录结构
Android自定义控件提供颜色属性动态改变控件颜色_第4张图片
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;
    }
}

效果
Android自定义控件提供颜色属性动态改变控件颜色_第5张图片

你可能感兴趣的:(android开发)