实现自定义Button有两种方式,


1. 继承View,在里面自己去实现onDraw(), onMeasure(), onClickListener()等方法。这种方式比较灵活,可以实现复杂的需求。


代码样例如下:


public class CustomButton extends View{ 



 private final static int WIDTH_PADDING = 8; 



 private final static int HEIGHT_PADDING = 10; 



 private final String label; 



 private final int imageResId; 



 private final Bitmap image; 



 //private final InternalListener listenerAdapter = new InternalListener(); 



 public CustomButton(Context context, int resImage, String label) 



        { 



            super(context); 



           this.label = label; 



           this.imageResId = resImage; 



            this.image = BitmapFactory.decodeResource(context.getResources(), 



                   imageResId); 



   



            setFocusable(true); 



            setBackgroundColor(Color.WHITE); 



            //setOnClickListener(listenerAdapter); 



            setClickable(true); 



        } 



 @Override 



 protected void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect){ 



 this.setBackgroundColor(Color.GREEN); 



 } 



 @Override 



 protected void onDraw(Canvas canvas) 



     { 



          Paint textPaint = new Paint(); 



          textPaint.setColor(Color.BLACK); 



          canvas.drawBitmap(image, WIDTH_PADDING / 2, HEIGHT_PADDING / 2, null); 



           canvas.drawText(label, WIDTH_PADDING / 2, (HEIGHT_PADDING / 2) + 



                   image.getHeight() + 8, textPaint); 



      } 



 @Override 



        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 



         { 



          setMeasuredDimension(measureWidth(widthMeasureSpec), 



                  measureHeight(heightMeasureSpec)); 



          } 



 private int measureWidth(int measureSpec) 



      { 



         int preferred = image.getWidth() * 2; 



          return getMeasurement(measureSpec, preferred); 



      } 



      



      private int measureHeight(int measureSpec) 



      { 



           int preferred = image.getHeight() * 2; 



           return getMeasurement(measureSpec, preferred); 



      } 



      private int getMeasurement(int measureSpec, int preferred) 



      { 



               int specSize = MeasureSpec.getSize(measureSpec); 



               int measurement = 0; 



             



               switch(MeasureSpec.getMode(measureSpec)) 



              { 



                   case MeasureSpec.EXACTLY: 



                       // This means the width of this view has been given. 



                       measurement = specSize; 



                       break; 



                  case MeasureSpec.AT_MOST: 



                      // Take the minimum of the preferred size and what 



                       // we were told to be. 



                      measurement = Math.min(preferred, specSize); 



                      break; 



                 default: 



                      measurement = preferred; 



                     break; 



              } 



        



              return measurement; 



          } 



     /* public void setOnClickListener(ClickListener newListener) 



           { 



               listenerAdapter.setListener(newListener); 



           }*/ 



      public String getLabel() 



           { 



               return label; 



           } 



          



           /** 



            * Returns the resource id of the image. 



            */ 



           public int getImageResId() 



           { 



               return imageResId; 



           } 



          /* private class InternalListener implements View.OnClickListener 



                { 



                    private ClickListener listener = null; 



               



                    



                    public void setListener(ClickListener newListener) 



                    { 



                        listener = newListener; 



                    } 



                   



                    @Override 



                    public void onClick(View v) 



                    { 



                        if (listener != null) 



                        { 



                            listener.onClick(CustomImageButton.this); 



                        } 



                    } 



                }*/ 



    



 }


2. 另一种还是保持原来button的基本功能,只需要改变button在不同状态下的样式,那么就可以这样来做:

Android开发应用中,默认的Button是由系统渲染和管理大小的。而我们看到的成功的移动应用,都是有着酷炫的外观和使用体验的。因此,我们在开发产品的时候,需要对默认按钮进行美化。在本篇里,笔者结合在应用开发中的经验,探讨一下自定义背景的按钮、自定义形状按钮的实现方法。

首先看实现效果截图:


自定义背景的按钮目前有2种方式实现,矢量和位图。



1. 矢量图形绘制的方式

矢量图形绘制的方式实现简单,适合对于按钮形状和图案要求不高的场合。步骤如下:

(a) 使用xml定义一个圆角矩形,外围轮廓线实线、内填充渐变色,xml代码如下。


1. //bg_alibuybutton_default.xml  
2. <?xml version="1.0" encoding="utf-8"?>  
3. <layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
4. <item>  
5. <shape android:shape="rectangle">   
6. <solid android:color="#FFEC7600" />  
7. <corners  
8. android:topLeftRadius="5dip"  
9. android:topRightRadius="5dip"  
10. android:bottomLeftRadius="5dip"  
11. android:bottomRightRadius="5dip" />  
12. </shape>  
13. </item>  
14. <item android:top="1px" android:bottom="1px" android:left="1px" android:right="1px">  
15. <shape>  
16. <gradient   
17. android:startColor="#FFEC7600" android:endColor="#FFFED69E"   
18. android:type="linear" android:angle="90"  
19. android:centerX="0.5" android:centerY="0.5" />  
20. <corners  
21. android:topLeftRadius="5dip"  
22. android:topRightRadius="5dip"  
23. android:bottomLeftRadius="5dip"  
24. android:bottomRightRadius="5dip" />  
25. </shape>  
26. </item>    
27. </layer-list>


同样定义bg_alibuybutton_pressed.xml和bg_alibuybutton_selected.xml,内容相同,就是渐变颜色不同,用于按钮按下后的背景变化效果。

(b) 定义按钮按下后的效果变化描述文件drawable/bg_alibuybutton.xml,代码如下。



    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
    3. <item android:state_pressed="true"  
    4. android:drawable="@drawable/bg_alibuybutton_pressed" />  
    5. <item android:state_focused="true"  
    6. android:drawable="@drawable/bg_alibuybutton_selected" />  
    7. <item android:drawable="@drawable/bg_alibuybutton_default" />  
    8. </selector>


    (c) 在你需要的界面定义文件中,如layout/main.xml中定义一个Button控件。


    1. <Button  
    2. android:layout_width="120dip"  
    3. android:layout_height="40dip"  
    4. android:text="矢量背景按钮"       android:background="@drawable/bg_alibuybutton" />

    这样,自定义背景的按钮就可以使用了,在实现onClick方法后就可以响应操作。

     

    2. 9-patch图片背景方式

    此种方法相对复杂繁琐,但可以制作出更多、更复杂样式的按钮图样。

    什么是9-patch格式呢?

    9-patch格式,是在Android中特有的一种PNG图片格式,以"***.9.png"结尾。此种格式的图片定义了可以伸缩拉伸的区域和文字显示区域,这样,就可以在Android开发中对非矢量图进行拉伸而仍然保持美观。如果使用位图而没有经过9-patch处理的话,效果就会想第一张截图中的“普通图片背景按钮”那样被无情的拉伸,影响效果。Android中大量用了这种技术,默认的按钮的背景就是用了类似的方法实现的。我们看一下google官方的描述:


     

    该格式相对于一般PNG图片来说,多了上下左右各一条1px的黑线。左、上黑线隔开了9个格子,当中一个格子(见上图Strechable Area区域)声明为可以进行拉伸。右、下两条黑线所定义的Paddingbox区域是在该图片当做背景时,能够在图片上填写文字的区域。每条黑线都是可以不连续的,这样就可以定义出很多自动拉伸的规格。Android sdk中提供了设置的工具,启动命令位于:$ANDROID_SDK/tools/draw9patch.bat,使用它对于原始PNG进行设置9-patch格式,非常方便,如下图。


            

    draw9patch工具的右侧是能够看到各方向拉伸后的效果图,你所要做的就是在图上最外侧一圈1px宽的像素上涂黑线。

    注意,在draw9patch.bat第一次运行时,sdk2.2版本上会报错:Java.lang.NoClassDefFoundError:org/jdesktop/swingworker/SwingWorker。需要下载swing-worker-1.1.jar,放入$android_sdk/tools/lib路径下,成功运行。

    此种方法实现的步骤如下。

    (a) 使用draw9patch.bat作完图片后,得到两张按钮背景,分别是正常和按下状态下的,命名为bg_btn.9.png和bg_btn_2.9.png。

    (b) 编写图片使用描述文件bg_9patchbutton.xml。


    1. // in bg_9patchbutton.xml  
    2. <?xml version="1.0" encoding="UTF-8"?>  
    3. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
    4. <item android:state_pressed="true"  
    5. android:drawable="@drawable/bg_btn_2" />  
    6. <item android:state_focused="true"  
    7. android:drawable="@drawable/bg_btn_2" />  
    8. <item android:drawable="@drawable/bg_btn" />  
    9. </selector>

    (c) 在界面定义文件 layout/main.xml中添加Button、ImageButton按钮控件的定义。Button、ImageButton都是可以使用背景属性的。


      1. <Button  
      2. android:layout_width="120dip"  
      3. android:layout_height="40dip"  
      4. android:text="9-patch图片背景按钮"  
      5. android:background="@drawable/bg_9patchbutton" />  
      6. <Button  
      7. android:layout_width="200dip"  
      8. android:layout_height="40dip"  
      9. android:text="9-patch图片背景按钮"  
      10. android:background="@drawable/bg_9patchbutton" />  
      11. <Button  
      12. android:layout_width="120dip"  
      13. android:layout_height="80dip"  
      14. android:text="9-patch图片背景按钮"  
      15. android:background="@drawable/bg_9patchbutton" />  
      16. <ImageButton  
      17. android:layout_width="120dip"  
      18. android:layout_height="40dip"  
      19. android:src="@drawable/bg_9patchbutton"  
      20. android:scaleType="fitXY"  
      21. android:background="@android:color/transparent" />


      以上2种实现按钮的美化,都是标准的矩形按钮为基础。在一些应用中我们可以看到漂亮的自定义形状的异形按钮,这是怎么实现的呢?经过一番研究和实践,找出了一种方便的方法,就是使用ImageButton加上9-patch就可以实现漂亮的自动延伸效果。

       

      3. 自定义形状、颜色、图样的按钮的实现

      步骤如下。

      (a) 设计一张自定义形状风格背景的图片,如下图。


      (b) 未点击和按下后的状态各做一张,形成一套图片,如下图。


       forward.png   

       forward2.png

      (c) 创建和编写按钮不同状态的图片使用描述文件drawable/ib_forward.xml

      1. // ib_forward.xml  
      2. <?xml version="1.0" encoding="UTF-8"?>  
      3. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
      4. <item android:state_pressed="true"  
      5. android:drawable="@drawable/forward2" />  
      6. <item android:state_focused="true"  
      7. android:drawable="@drawable/forward2" />  
      8. <item android:drawable="@drawable/forward" />  
      9. </selector>


      (d) 在界面定义文件 layout/main.xml中添加ImageButton按钮控件的定义。

      1. // in layout/main.xml  
      2. <ImageButton  
      3. android:layout_width="80dip"  
      4. android:layout_height="40dip"  
      5. android:src="@drawable/ib_forword"  
      6. android:scaleType="fitXY"  
      7. android:background="@android:color/transparent" />