本文将介绍一种有效改变Android按钮颜色的方法。

按钮可以在状态改变时改变其颜色(例如按下,禁用,高亮显示)。但是,这需要一一说明每个状态。这篇文章将提供你一个根据状态变化轻松改变按钮颜色的方法。如果你正在写自定义视图,那么不妨也来读一读,因为中间我会涉及到如何用自定义属性实现自定义视图的相关内容。

如何实现

Android提供了灵活的绘制选择机制,可根据视图状态转变视图外观。每个状态通过一个单独的部分而存在。例如:在正常、禁用、按下、高亮状态下的按钮有着不同的背景颜色。请看下面的代码示例:

button_1_background.xml

?
1 2 3 4 5 6 7 8 "http://schemas.android.com/apk/res/android" > "@drawable/button_1_selected" android:state_pressed= "true" /> "@drawable/button_1_focused" android:state_focused= "true" /> "@drawable/button_1_normal" />

每个状态drawables的属性(button_1_selected, button_1_focused,button_1_normal)必须定义在相应的在drawables目录下:

button_1_normal.xml

?
1 2 3 4 "http://schemas.android.com/apk/res/android" >      "@color/button_1_normal_background" />      "10dip" />

button_1_focused.xml

?
1 2 3 4 "http://schemas.android.com/apk/res/android" >      "@color/button_1_focused_background" />      "10dip" />

button_1_selected.xml

?
1 2 3 4 "http://schemas.android.com/apk/res/android" >      "@color/button_1_selected_background" />      "10dip" />

然后设置按钮背景:

?
1 android:background= "@drawable/button_1_background"

这种方法非常灵活。但是,当你的app有许多按钮,而每个按钮的颜色又各不相同时,维护每个按钮的上述所有XML文件就会变得异常困难起来。如果你改变正常状态的按钮颜色,那么你必须改变其他状态的颜色。在上面的例子中,每个按钮需要4个XML文件。那么如果你的应用程序有10个或更多个按钮呢?

为了清楚说明我的意思,请看下面的截图:

这些截图来自于一款免费产品BMEX。

这两张图片分别是app的主屏幕和发送屏幕。两个屏幕都采用了Metro风格。每个屏幕都有6个不同颜色的按钮。并且按钮的颜色会根据状态的改变而改变。共计12个按钮,所以我们需要12个drawable selector XML文件和24个drawable state XML文件。并且随着app的发展,软件还得允许新的屏幕和新的按钮的添加。维护这些内容可不是一项简单的任务。

为了使过程更加简单和高效,我们另寻了一种更有效的解决方案——并且已经实现在自定义按钮视图中。这是一个容易初始化的按钮。我们称之为RoundButton,因为它支持圆角。

在另一个产品中,我们需要高亮功能,但是,又不想因此单独创建自定义视图。所以,我们把它添加到RoundButton中。请看下面的截图:

正如你所见,我们可以选择也可以不选屏幕上的按钮(顶部的列表图表和每个元素后面的添加图标)。当按钮被选中后,它的highlighted状态就被设置为true,反之,则为false。并且按钮的外观会作适当改变。在上面的例子中,高亮模式使用了“image”。在这种模式下,图像的可见象素会被绘制为高亮颜色。

首先,我们为RoundButton定义属性集。这是一组可以通过布局XML设置的属性。

attrs_round_button.xml

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18        "RoundButton" >          "image" format= "reference" />          "bgcolor" format= "color" />          "text" format= "string" />            "radius" format= "float" />            "highlightColor" format= "color" />          "highlightMode" format= "enum" >              < enum name= "none" value= "0" />              < enum name= "image" value= "1" />              < enum name= "background" value= "2" />                

我们增加了 image,bgcolor,text,边框圆角半径,highlightColor和highlightMode属性。按下状态的颜色会从bgcolor导出(后面会描述的)。

实现按钮

首先,我们需要实现构造函数和解析参数。我们创建了3个不同的构造函数:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public RoundButton(Context context, AttributeSet attrs,  int defStyle) {      super (context, attrs, defStyle);        init(attrs, defStyle); }   public RoundButton(Context context, AttributeSet attrs) {      super (context, attrs);        init(attrs,  0 ); }   public RoundButton(Context context) {      super (context);        init( null 0 ); }

所有这些构造函数调用init方法。

现在,我们需要实现init方法。它将属性集和默认样式作为输入参数。在init方法中,我们获取属性值,并初始化内部变量。如果属性集为null,那就使用默认值。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private void init(AttributeSet attrs,  int defStyle) {      Drawable image;      int bgcolor;      String text;        if (attrs !=  null ) {          final TypedArray a = getContext().obtainStyledAttributes(attrs,                  R.styleable.RoundButton, defStyle,  0 );            image = a.getDrawable(R.styleable.RoundButton_image);            bgcolor = a.getColor(R.styleable.RoundButton_bgcolor,  0xffffffff );            text = a.getString(R.styleable.RoundButton_text);            radius = a.getFloat(R.styleable.RoundButton_radius,  12 .0f);            highlightMode = HighlightMode.getValue(a.getInt          (R.styleable.RoundButton_highlightMode, HighlightMode.None.ordinal()));            highlightColor = a.getColor(R.styleable.RoundButton_highlightColor,  0xff00b5ff );            a.recycle();      }      else {          image =  null ;            text =  "" ;            bgcolor =  0xff808080 ;            radius =  12 .0f;            highlightMode = HighlightMode.None;            highlightColor =  0xff00b5ff ;      }        init(image, bgcolor, text); }

然后,我们创建另一个init方法。这个方法用于创建对象,并需要渲染按钮的内容。 此处的init方法声明为public,因为创建RoundButton时需要调用它。它创建了背景和按下时的“喷漆(paint)”——绘制正常和按下状态时的背景的对象。按下的颜色选取比bgcolor更亮的颜色。使颜色变亮的的方法,稍后会进行说明。这里初始化了高亮模式。如果背景设置为高亮,那就创建高亮喷漆,用于绘制高亮时的按钮背景。如果图像模式设置为高亮,那就创建高亮图像。在createHighlightImage方法中创建图像的代码,之后会一一给出。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public void init(Drawable image,  int bgcolor, String text) {      this .image = image;        bgpaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      bgpaint.setColor(bgcolor);        pressedBgpaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      pressedBgpaint.setColor(brighter(bgcolor));        if (text ==  null )          text =  "" ;        this .text = text;        textPaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      textPaint.setColor( 0xffffffff );      textPaint.setTextAlign(Paint.Align.CENTER);      textPaint.setTextSize(pixelsToSp(getContext(), textSize));        if (highlightMode == HighlightMode.Background) {          highlightPaint =  new Paint(Paint.ANTI_ALIAS_FLAG);          highlightPaint.setColor(highlightColor);      }      else if (highlightMode == HighlightMode.Image) {          highlightImage = createHighlightImage();      }        setClickable( true ); }

要获得按下状态的色值,我们创建了brighter方法。它将颜色作为参数,并返回比该颜色更亮的颜色。这个方法也很简单:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public void init(Drawable image,  int bgcolor, String text) {      this .image = image;        bgpaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      bgpaint.setColor(bgcolor);        pressedBgpaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      pressedBgpaint.setColor(brighter(bgcolor));        if (text ==  null )          text =  "" ;        this .text = text;        textPaint =  new Paint(Paint.ANTI_ALIAS_FLAG);      textPaint.setColor( 0xffffffff );      textPaint.setTextAlign(Paint.Align.CENTER);      textPaint.setTextSize(pixelsToSp(getContext(), textSize));        if (highlightMode == HighlightMode.Background) {          highlightPaint =  new Paint(Paint.ANTI_ALIAS_FLAG);          highlightPaint.setColor(highlightColor);      }      else if (highlightMode == HighlightMode.Image) {          highlightImage = createHighlightImage();      }        setClickable( true ); }

接下来的方法是createHighlightImage。当图像设置为高亮模式时,它会调用上面所示的方法。但是开头有一些比较棘手的代码。它需要得到图像的像素。然后处理像素 ——如果像素是不透明的(alpha != 0),就用高亮色值取代它,但是如果像素是透明的,那就不用改动。通过这种操作,我们创建了更高亮的图像。然后,我们将修改后的像素放回位图。并且在方法的最后,创建并返回BitmapDrawable。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private Drawable createHighlightImage() {      int width = image.getIntrinsicWidth();      int height = image.getIntrinsicHeight();        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);        Canvas canvas =  new Canvas(bitmap);      image.setBounds( 0 0 , width, height);      image.draw(canvas);        int count = bitmap.getWidth() * bitmap.getHeight();      int pixels[] =  new int [count];        bitmap.getPixels(pixels,  0 , bitmap.getWidth(),  0 0 , bitmap.getWidth(), bitmap.getHeight());      for ( int n =  0 ; n < count; n++) {          boolean v = (Color.alpha(pixels[n])) !=  0 ;            if (v) {              int pixel = pixels[n];                int alpha = Color.alpha(pixel);              int red = Color.red(highlightColor);              int green = Color.green(highlightColor);              int blue = Color.blue(highlightColor);                int color = Color.argb(alpha, red, green, blue);                pixels[n] = color;          }      }        bitmap.setPixels(pixels,  0 , bitmap.getWidth(),  0 0 , bitmap.getWidth(), bitmap.getHeight());        return new BitmapDrawable(getResources(), bitmap); }

为了处理状态变化,我们需要处理触摸事件。所以需要实现触摸处理。当我们触摸按钮时,它的状态就会变为pressed(按下),并重绘按钮中的内容。当按钮没有被触摸,那它的pressed标志就设置为false,并重绘按钮中的内容。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Override public boolean onTouchEvent(MotionEvent event) {      int action = event.getActionMasked();        switch (action) {      case MotionEvent.ACTION_DOWN:          pressed =  true ;          invalidate();          break ;               case MotionEvent.ACTION_UP:          pressed =  false ;          invalidate();          break ;      case MotionEvent.ACTION_CANCEL:      case MotionEvent.ACTION_OUTSIDE:      case MotionEvent.ACTION_HOVER_EXIT:          pressed =  false ;          invalidate();          break ;      }        return super .onTouchEvent(event); }

然后,我们实现onDraw按钮方法。此方法绘制了按钮的内容。自定义视图首次展示以及每次重绘时就调用这个onDraw方法。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 protected void onDraw(Canvas canvas) {      RectF bounds =  new RectF( 0 0 , getWidth(), getHeight());        Drawable image =  null ;      Paint bgPaint =  null ;        switch (highlightMode) {      case None:          image =  this .image;          bgPaint = pressed ? pressedBgpaint :  this .bgpaint;          break ;      case Background:          image =  this .image;          if (pressed)              bgPaint = pressedBgpaint;          else              bgPaint = highlighted ? highlightPaint :  this .bgpaint;          break ;      case Image:          image = highlighted ? highlightImage :  this .image;          bgPaint = pressed ? pressedBgpaint :  this .bgpaint;          break ;      }        if (radius !=  0 .0f)          canvas.drawRoundRect(bounds, radius, radius, bgPaint);      else          canvas.drawRect(bounds, bgPaint);        Rect textBounds =  new Rect();      if (text.length() >  0 )          textPaint.getTextBounds(text,  0 , text.length(), textBounds);        float h_dst = ((image !=  null ) ? image.getMinimumHeight() +      ((text.length() >  0 ) ? spacing :  0 ) :  0 ) + textBounds.height();        float xd = (bounds.width() - ((image !=  null ) ? image.getMinimumWidth() :  0 )) /  2 ;      float yd = (bounds.height() - h_dst) /  2 ;        if (image !=  null ) {          image.setBounds(( int ) xd, ( int ) yd, ( int )          (xd + image.getMinimumWidth()), ( int ) (yd + image.getMinimumHeight()));          image.draw(canvas);      }        float xt = (bounds.width() -  0 * textBounds.width()) /  2 ;      float yt = yd + ((image !=  null ) ? image.getMinimumHeight() +      ((text.length() >  0 ) ? spacing :  0 ) : textBounds.height()); // + textBounds.height();        canvas.drawText(text, xt, yt, textPaint);        if (checked && checkable && checkedImage !=  null ) {          checkedImage.setBounds(( int ) (bounds.width() -          checkedImage.getMinimumWidth()), ( int ) (bounds.height() - checkedImage.getMinimumHeight()),                  ( int ) bounds.width(), ( int ) bounds.height());          checkedImage.draw(canvas);      } }

用法

为了整合RoundButton到代码,你需要下载源代码文件。在源代码文件中,有Eclipse项目,源代码和XML资源文件。你可以将它们复制到你的app项目中。或者编译RoundButton项目并将其作为库添加到你的项目。

如果你使用的是可视化编辑器,那就直接从控件列表中选择RoundButton,在添加它之后,设置其属性。

除了可视化编辑器,RoundButton既可以从布局XML,也可以从代码中插入。从布局XML添加的话,你可以这么使用。示例如下:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 "http://schemas.android.com/apk/res/android"      android:layout_width= "match_parent"      android:layout_height= "wrap_content"      android:focusable= "false"      android:focusableInTouchMode= "false"      android:descendantFocusability= "blocksDescendants"      android:orientation= "horizontal"      xmlns:app= "http://schemas.android.com/apk/res/com.bitgriff.bamp" >                 android:id= "@+id/button"          app:radius= "0"          app:image= "@drawable/ic_addtomedialibrary"          app:bgcolor= "@color/transparent"          app:highlightMode= "image"          android:layout_width= "40dip"          android:layout_height= "80dip"          android:layout_centerVertical= "true"          android:layout_alignParentRight= "true" />  

从代码添加RoundButton,可以创造新的RoundButton实例。调用它的init方法传递图像(可为null),bgcolo和text。并添加RoundButton到你的ViewGroup:

?
1 2 roundButton =  new RoundButton(context); roundButton.init(image, bgcolor, text);

进一步设想

此外,我们还可以改变RoundButton的形状。例如,制作圆形按钮,正如现在很多Android app中所见的那样。也可能配置图像位置(left、right、top、bottom)。等等。

总结

这篇文章主要描述了如何实现根据状态改变背景的自定义按钮。这个简单的组件能为我们节省很多时间。希望能对你有用。

许可证

这篇文章,以及相关源代码和文件,都是经过The BSD License许可的。

译文链接: http://www.codeceo.com/article/android-button-color.html
翻译作者: 码农网  – 小峰

更多相关文章

  1. Android双击返回键退出程序的实现方法
  2. android google 分屏 多窗口 按home键界面错乱故障分析(一)分屏的
  3. 客觀評 Android、iOS、WP7
  4. 移动语音引擎相关开发笔记
  5. Android使用JNI实现Java与C之间传递数据
  6. Android移动应用界面的模板化设计
  7. Android方法数
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 【android学习笔记】收藏的网上查找的and
  2. Android TV 焦点控制逻辑
  3. java
  4. android sdk 文件目录含义介绍
  5. __builtin_clz(); __builtin_ctz(); __bu
  6. android 布局边框阴影1dp
  7. 【源码分享下载】每日更新之Android快速
  8. android通知栏进度条
  9. Android 之 shape和selector用法介绍
  10. Android(安卓)Color值汇总