zoukankan      html  css  js  c++  java
  • Android自定义控件系列(一)—Button七十二变

    转载请注明出处:http://www.cnblogs.com/landptf/p/6290791.html

    忙了一段时间,终于有时间整理整理之前所用到的一些知识,分享给大家,希望给同学们有些帮助,同时也是对自己的知识有个巩固的过程。

    在Android的开发中比较常用的控件就是Button了,但是我们平时使用Button时是怎样来设置按下和抬起显示不同的效果呢?我想一般的实现方式就是定义一个selector的xml文件,然后在里面根据不同的state来设置不同的图片,但是当Button控件非常多的时候,就要写对应数量的xml文件,导致大码非常臃肿。

    今天我们换种方式来改变这个样式,只需要两行代码即可实现按下的效果,同时支持圆角和圆形的按钮的样式。先看下效果图,这是我写的一个demo

    接下来讲一下主要代码: 
    第一步 自定义属性 
    在res/values/目录下新建attrs.xml文件

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <resources>
     3     <!--公共属性-->
     4     <attr name="backColor" format="color" />
     5     <attr name="backColorPress" format="color" />
     6     <attr name="backGroundImage" format="reference" />
     7     <attr name="backGroundImagePress" format="reference" />
     8     <attr name="textColor" format="color" />
     9     <attr name="textColorPress" format="color" />
    10 
    11     <declare-styleable name="buttonM">
    12         <attr name="backColor" />
    13         <attr name="backColorPress" />
    14         <attr name="backGroundImage"  />
    15         <attr name="backGroundImagePress" />
    16         <attr name="textColor" />
    17         <attr name="textColorPress" />
    18         <attr name="fillet" format="boolean" />
    19         <attr name="radius" format="float" />
    20         <attr name="shape">
    21             <enum name="rectangle" value="0" />
    22             <enum name="oval" value="1" />
    23             <enum name="line" value="2" />
    24             <enum name="ring" value="3" />
    25         </attr>
    26     </declare-styleable>
    27 
    28 </resources>

    具体属性的含义在java代码中都有描述 
    第二步 创建ButtonM类使其继承Button,代码如下:

      1 package com.landptf.view;
      2 
      3 import android.content.Context;
      4 import android.content.res.ColorStateList;
      5 import android.content.res.TypedArray;
      6 import android.graphics.drawable.Drawable;
      7 import android.graphics.drawable.GradientDrawable;
      8 import android.util.AttributeSet;
      9 import android.view.MotionEvent;
     10 import android.view.View;
     11 import android.widget.Button;
     12 
     13 import com.landptf.R;
     14 
     15 /**
     16  * Created by landptf on 2016/10/25.
     17  * 自定义Button,支持圆角矩形,圆形按钮等样式,可通过配置文件改变按下后的样式
     18  * 若通过代码设置圆角或者圆形,需要先调用setFillet方法将fillet设置为true
     19  */
     20 public class ButtonM extends Button {
     21     private static String TAG = "ButtonM";
     22     /**
     23      * 按钮的背景色
     24      */
     25     private int backColor = 0;
     26     /**
     27      * 按钮被按下时的背景色
     28      */
     29     private int backColorPress = 0;
     30     /**
     31      * 按钮的背景图片
     32      */
     33     private Drawable backGroundDrawable = null;
     34     /**
     35      * 按钮被按下时显示的背景图片
     36      */
     37     private Drawable backGroundDrawablePress = null;
     38     /**
     39      * 按钮文字的颜色
     40      */
     41     private ColorStateList textColor = null;
     42     /**
     43      * 按钮被按下时文字的颜色
     44      */
     45     private ColorStateList textColorPress = null;
     46     private GradientDrawable gradientDrawable = null;
     47     /**
     48      * 是否设置圆角或者圆形等样式
     49      */
     50     private boolean fillet = false;
     51     /**
     52      * 标示onTouch方法的返回值,用来解决onClick和onTouch冲突问题
     53      */
     54     private boolean isCost = true;
     55 
     56     public ButtonM(Context context) {
     57         super(context, null);
     58     }
     59 
     60     public ButtonM(Context context, AttributeSet attrs) {
     61         this(context, attrs, 0);
     62     }
     63 
     64     public ButtonM(Context context, AttributeSet attrs, int defStyle) {
     65         super(context, attrs, defStyle);
     66         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.buttonM, defStyle, 0);
     67         if (a != null) {
     68             //设置背景色
     69             ColorStateList colorList = a.getColorStateList(R.styleable.buttonM_backColor);
     70             if (colorList != null) {
     71                 backColor = colorList.getColorForState(getDrawableState(), 0);
     72                 if (backColor != 0) {
     73                     setBackgroundColor(backColor);
     74                 }
     75             }
     76             //记录按钮被按下时的背景色
     77             ColorStateList colorListPress = a.getColorStateList(R.styleable.buttonM_backColorPress);
     78             if (colorListPress != null){
     79                 backColorPress = colorListPress.getColorForState(getDrawableState(), 0);
     80             }
     81             //设置背景图片,若backColor与backGroundDrawable同时存在,则backGroundDrawable将覆盖backColor
     82             backGroundDrawable = a.getDrawable(R.styleable.buttonM_backGroundImage);
     83             if (backGroundDrawable != null){
     84                 setBackgroundDrawable(backGroundDrawable);
     85             }
     86             //记录按钮被按下时的背景图片
     87             backGroundDrawablePress = a.getDrawable(R.styleable.buttonM_backGroundImagePress);
     88             //设置文字的颜色
     89             textColor = a.getColorStateList(R.styleable.buttonM_textColor);
     90             if (textColor != null){
     91                 setTextColor(textColor);
     92             }
     93             //记录按钮被按下时文字的颜色
     94             textColorPress = a.getColorStateList(R.styleable.buttonM_textColorPress);
     95             //设置圆角或圆形等样式的背景色
     96             fillet = a.getBoolean(R.styleable.buttonM_fillet, false);
     97             if (fillet){
     98                 getGradientDrawable();
     99                 if (backColor != 0) {
    100                     gradientDrawable.setColor(backColor);
    101                     setBackgroundDrawable(gradientDrawable);
    102                 }
    103             }
    104             //设置圆角矩形的角度,fillet为true时才生效
    105             float radius = a.getFloat(R.styleable.buttonM_radius, 0);
    106             if (fillet && radius != 0){
    107                 setRadius(radius);
    108             }
    109             //设置按钮形状,fillet为true时才生效
    110             int shape = a.getInteger(R.styleable.buttonM_shape, 0);
    111             if (fillet && shape != 0) {
    112                 setShape(shape);
    113             }
    114             a.recycle();
    115         }
    116         setOnTouchListener(new OnTouchListener() {
    117             @Override
    118             public boolean onTouch(View arg0, MotionEvent event) {
    119                 //根据touch事件设置按下抬起的样式
    120                 return setTouchStyle(event.getAction());
    121             }
    122         });
    123     }
    124 
    125     /**
    126      * 根据按下或者抬起来改变背景和文字样式
    127      * @param state
    128      * @return isCost
    129      *  为解决onTouch和onClick冲突的问题
    130      *  根据事件分发机制,如果onTouch返回true,则不响应onClick事件
    131      *  因此采用isCost标识位,当用户设置了onClickListener则onTouch返回false
    132      */
    133     private boolean setTouchStyle(int state){
    134         if (state == MotionEvent.ACTION_DOWN) {
    135             if (backColorPress != 0) {
    136                 if (fillet){
    137                     gradientDrawable.setColor(backColorPress);
    138                     setBackgroundDrawable(gradientDrawable);
    139                 }else {
    140                     setBackgroundColor(backColorPress);
    141                 }
    142             }
    143             if (backGroundDrawablePress != null) {
    144                 setBackgroundDrawable(backGroundDrawablePress);
    145             }
    146             if (textColorPress != null) {
    147                 setTextColor(textColorPress);
    148             }
    149         }
    150         if (state == MotionEvent.ACTION_UP) {
    151             if (backColor != 0) {
    152                 if (fillet){
    153                     gradientDrawable.setColor(backColor);
    154                     setBackgroundDrawable(gradientDrawable);
    155                 }else {
    156                     setBackgroundColor(backColor);
    157                 }
    158             }
    159             if (backGroundDrawable != null) {
    160                 setBackgroundDrawable(backGroundDrawable);
    161             }
    162             if (textColor != null) {
    163                 setTextColor(textColor);
    164             }
    165         }
    166         return isCost;
    167     }
    168 
    169     /**
    170      * 重写setOnClickListener方法,解决onTouch和onClick冲突问题
    171      * @param l
    172      */
    173     @Override
    174     public void setOnClickListener(OnClickListener l) {
    175         super.setOnClickListener(l);
    176         isCost = false;
    177     }
    178 
    179     /**
    180      * 设置按钮的背景色
    181      * @param backColor
    182      */
    183     public void setBackColor(int backColor) {
    184         this.backColor = backColor;
    185         if (fillet){
    186             gradientDrawable.setColor(backColor);
    187             setBackgroundDrawable(gradientDrawable);
    188         }else {
    189             setBackgroundColor(backColor);
    190         }
    191     }
    192 
    193     /**
    194      * 设置按钮被按下时的背景色
    195      * @param backColorPress
    196      */
    197     public void setBackColorPress(int backColorPress) {
    198         this.backColorPress = backColorPress;
    199     }
    200 
    201     /**
    202      * 设置按钮的背景图片
    203      * @param backGroundDrawable
    204      */
    205     public void setBackGroundDrawable(Drawable backGroundDrawable) {
    206         this.backGroundDrawable = backGroundDrawable;
    207         setBackgroundDrawable(backGroundDrawable);
    208     }
    209 
    210     /**
    211      * 设置按钮被按下时的背景图片
    212      * @param backGroundDrawablePress
    213      */
    214     public void setBackGroundDrawablePress(Drawable backGroundDrawablePress) {
    215         this.backGroundDrawablePress = backGroundDrawablePress;
    216     }
    217 
    218     /**
    219      * 设置文字的颜色
    220      * @param textColor
    221      */
    222     public void setTextColor(int textColor) {
    223         if (textColor == 0) return;
    224         this.textColor = ColorStateList.valueOf(textColor);
    225         //此处应加super关键字,调用父类的setTextColor方法,否则会造成递归导致内存溢出
    226         super.setTextColor(this.textColor);
    227     }
    228 
    229     /**
    230      * 设置按钮被按下时文字的颜色
    231      * @param textColorPress
    232      */
    233     public void setTextColorPress(int textColorPress) {
    234         if (textColorPress == 0) return;
    235         this.textColorPress = ColorStateList.valueOf(textColorPress);
    236     }
    237 
    238     /**
    239      * 设置按钮是否设置圆角或者圆形等样式
    240      * @param fillet
    241      */
    242     public void setFillet(boolean fillet){
    243         this.fillet = fillet;
    244         getGradientDrawable();
    245     }
    246 
    247     /**
    248      * 设置圆角按钮的角度
    249      * @param radius
    250      */
    251     public void setRadius(float radius){
    252         if (!fillet) return;
    253         getGradientDrawable();
    254         gradientDrawable.setCornerRadius(radius);
    255         setBackgroundDrawable(gradientDrawable);
    256     }
    257 
    258     /**
    259      * 设置按钮的形状
    260      * @param shape
    261      */
    262     public void setShape(int shape){
    263         if (!fillet) return;
    264         getGradientDrawable();
    265         gradientDrawable.setShape(shape);
    266         setBackgroundDrawable(gradientDrawable);
    267     }
    268 
    269     private void getGradientDrawable() {
    270         if (gradientDrawable == null){
    271             gradientDrawable = new GradientDrawable();
    272         }
    273     }
    274 
    275 }

    注释基本上写的比较详细,下面主要说一下这里面涉及的一些知识点 
    1 关于onTouch返回值问题,如果返回true表示要消费该点击事件,后续的所有事件都交给他处理,同时onTouchEvent将不会执行,因此onClick也得不到执行,在这里通过重写setOnClickListener设置变量来改变返回值。具体关于View的事件分发机制可以查阅有关文档,网上很多这方面的教程。

    2 如果想要通过java代码来设置圆角或者圆形时,必须先设置setFillet(true),然后再设置背景色,形状或者角度等参数。通过xml文件则无限制

    最后讲一下怎么使用,这里以设置圆角矩形为例,分别通过xml和java代码实现,其他的可参考源码。

    1 xml

     1 <com.landptf.view.ButtonM
     2     android:id="@+id/btm_radius_color_xml"
     3     android:layout_width="0dp"
     4     android:layout_height="50dp"
     5     android:layout_weight="1"
     6     android:gravity="center"
     7     android:text="点击改变背景色"
     8     landptf:backColor="#ff3300"
     9     landptf:backColorPress="#ff33ff"
    10     landptf:fillet="true"
    11     landptf:radius="30"
    12     landptf:textColor="@android:color/white" />

    2 java

     1 ButtonM btmRadiusColorJava = (ButtonM) findViewById(R.id.btm_radius_color_java);
     2 if (btmRadiusColorJava != null) {
     3     btmRadiusColorJava.setFillet(true);
     4     btmRadiusColorJava.setRadius(30);
     5     btmRadiusColorJava.setTextColor(Color.parseColor("#ffffff"));
     6     btmRadiusColorJava.setBackColor(Color.parseColor("#ff3300"));
     7     btmRadiusColorJava.setBackColorPress(Color.parseColor("#ff33ff"));
     8     btmRadiusColorJava.setOnClickListener(new View.OnClickListener() {
     9         @Override
    10         public void onClick(View v) {
    11             Toast.makeText(ButtonMTestActivity.this, "java代码实现", Toast.LENGTH_SHORT).show();
    12         }
    13     });
    14 }

    代码已托管到开源中国的码云上,欢迎下载,地址:https://git.oschina.net/landptf/landptf.git

  • 相关阅读:
    oracle数据库使用PL/sql导入excel数据
    http协议详解之响应报文 3
    http详解之post 2
    http协议详解1
    tomcat配置https协议
    linux服务端导入oracle数据库.sql脚本
    fiddler查看http压缩格式传输的报文
    SurfaceView绘制录音波形图
    android 蓝牙SPP协议通信
    好用的Android屏幕适配
  • 原文地址:https://www.cnblogs.com/landptf/p/6290791.html
Copyright © 2011-2022 走看看