Android自定义控件系列—Button七十二变

弓背霞明剑照霜,秋风走马出咸阳。这篇文章主要讲述Android自定义控件系列—Button七十二变相关的知识,希望能为你提供帮助。
转载请注明出处:http://www.cnblogs.com/landptf/p/6290791.html
忙了一段时间,终于有时间整理整理之前所用到的一些知识,分享给大家,希望给同学们有些帮助,同时也是对自己的知识有个巩固的过程。
在android的开发中比较常用的控件就是Button了,但是我们平时使用Button时是怎样来设置按下和抬起显示不同的效果呢?我想一般的实现方式就是定义一个selector的xml文件,然后在里面根据不同的state来设置不同的图片,但是当Button控件非常多的时候,就要写对应数量的xml文件,导致大码非常臃肿。
今天我们换种方式来改变这个样式,只需要两行代码即可实现按下的效果,同时支持圆角和圆形的按钮的样式。先看下效果图,这是我写的一个demo

Android自定义控件系列—Button七十二变

文章图片

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

注释基本上写的比较详细,下面主要说一下这里面涉及的一些知识点 
1 关于onTouch返回值问题,如果返回true表示要消费该点击事件,后续的所有事件都交给他处理,同时onTouchEvent将不会执行,因此onClick也得不到执行,在这里通过重写setOnClickListener设置变量来改变返回值。具体关于View的事件分发机制可以查阅有关文档,网上很多这方面的教程。
2 如果想要通过java代码来设置圆角或者圆形时,必须先设置setFillet(true),然后再设置背景色,形状或者角度等参数。通过xml文件则无限制
【Android自定义控件系列—Button七十二变】最后讲一下怎么使用,这里以设置圆角矩形为例,分别通过xml和java代码实现,其他的可参考源码。
1 xml
1 < com.landptf.view.ButtonM 2android:id="@+id/btm_radius_color_xml" 3android:layout_width="0dp" 4android:layout_height="50dp" 5android:layout_weight="1" 6android:gravity="center" 7android:text="点击改变背景色" 8landptf:backColor="#ff3300" 9landptf:backColorPress="#ff33ff" 10landptf:fillet="true" 11landptf:radius="30" 12landptf:textColor="@android:color/white" />

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

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

    推荐阅读