출처 : http://kahdev.wordpress.com/2008/09/13/making-a-custom-android-button-using-a-custom-view/

 

Creating a custom view is as simple as inheriting from the View class and overriding the methods that need to be overridden. In this example, a custom button is implemented in this way. The button shall feature a labelled image (i.e. an image with text underneath).



1   public class CustomImageButton extends View {
2       private final static int WIDTH_PADDING = 8;
3       private final static int HEIGHT_PADDING = 10;
4       private final String label;
5       private final int imageResId;
6       private final Bitmap image;
7       private final InternalListener listenerAdapter = new InternalListener();
8

The constructor can take in the parameters to set the button image and label.


9       /**
10       * Constructor.
11       *
12       * @param context
13       *        Activity context in which the button view is being placed for.
14       *
15       * @param resImage
16       *        Image to put on the button. This image should have been placed
17       *        in the drawable resources directory.
18       *
19       * @param label
20       *        The text label to display for the custom button.
21       */
22      public CustomImageButton(Context context, int resImage, String label) 
23       {
24           super(context);
25           this.label = label;
26           this.imageResId = resImage;
27           this.image = BitmapFactory.decodeResource(context.getResources(),
28                  imageResId);
29  
30           setFocusable(true);
31           setBackgroundColor(Color.WHITE);
32
33           setOnClickListener(listenerAdapter);
34           setClickable(true);
35       }
36

With the constructor defined, there are a number of methods in the View class that needs to be overridden to this view behave like a button. Firstly, the onFocusChanged gets triggered when the focus moves onto or off the view. In the case of our custom button, we want the button to be “highlighted” when ever the focus is on the button.


37       /**
38       * The method that is called when the focus is changed to or from this
39       * view.
40       */
41       protected void onFocusChanged(boolean gainFocus, int direction,
42                      Rect previouslyFocusedRect)
43       {
44           if (gainFocus == true)
45           {
46               this.setBackgroundColor(Color.rgb(255, 165, 0));
47           }
48           else
49          {
50              this.setBackgroundColor(Color.WHITE);
51          }
52      }
53      

The method responsible for rendering the contents of the view to the screen is the draw method. In this case, it handles placing the image and text label on to the custom view.


54      /**
55       * Method called on to render the view.
56       */
57      protected void onDraw(Canvas canvas)
58      {
59          Paint textPaint = new Paint();
60          textPaint.setColor(Color.BLACK);
61          canvas.drawBitmap(image, WIDTH_PADDING / 2, HEIGHT_PADDING / 2, null);
62          canvas.drawText(label, WIDTH_PADDING / 2, (HEIGHT_PADDING / 2) +
63                  image.getHeight() + 8, textPaint);
64      }
65      

For the elements to be displayed correctly on the screen, Android needs to know the how big the custom view is. This is done through overriding the onMeasure method. The measurement specification parameters represent dimension restrictions that are imposed by the parent view.


 66        @Override
 67        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
 68        {
 69         setMeasuredDimension(measureWidth(widthMeasureSpec),
 70                 measureHeight(heightMeasureSpec));
 71        }
 72    

The call to setMeasuredDimension in the onMeasure method is important. The documentation states that the call is necessary to avoid a IllegalStateException.


 73     private int measureWidth(int measureSpec)
 74     {
 75         int preferred = image.getWidth() * 2;
 76         return getMeasurement(measureSpec, preferred);
 77     }
 78    
 79     private int measureHeight(int measureSpec)
 80     {
 81         int preferred = image.getHeight() * 2;
 82         return getMeasurement(measureSpec, preferred);
 83     }
 84    

To calculate the width and height measurements, I’ve chosen to keep the logic simple by using a simple formula to calculate the dimensions. This simple formula computes the dimensions based on the dimensions of the image. The measureSpec parameter specifies what restrictions are imposed by the parent layout.


 85     private int getMeasurement(int measureSpec, int preferred)
 86     {
 87         int specSize = MeasureSpec.getSize(measureSpec);
 88         int measurement = 0;
 89        
 90         switch(MeasureSpec.getMode(measureSpec))
 91         {
 92             case MeasureSpec.EXACTLY:
 93                 // This means the width of this view has been given.
 94                 measurement = specSize;
 95                 break;
 96             case MeasureSpec.AT_MOST:
 97                 // Take the minimum of the preferred size and what
 98                 // we were told to be.
 99                 measurement = Math.min(preferred, specSize);
100                 break;
101             default:
102                 measurement = preferred;
103                 break;
104         }
105    
106         return measurement;
107     }
108

To make the customised button useful, it needs to trigger some kind of action when it is clicked (i.e. a listener). The view class already defines methods for setting the listener, but a more specialised listener could be better suited to the custom button. For example, the specialised listener could pass back information on the instance of the custom button.


109     /**
110      * Sets the listener object that is triggered when the view is clicked.
111      *
112      * @param newListener
113      *        The instance of the listener to trigger.
114      */
115     public void setOnClickListener(ClickListener newListener)
116     {
117         listenerAdapter.setListener(newListener);
118     }
119    

If the custom listener passes information about this instance of the custom button, it may as well have accessors so listener implementation can get useful information about this custom button.


120     /**
121      * Returns the label of the button.
122      */
123     public String getLabel()
124     {
125         return label;
126     }
127    
128     /**
129      * Returns the resource id of the image.
130      */
131     public int getImageResId()
132     {
133         return imageResId;
134     }
135    

Finally, for our custom button class that is using a custom listener, the custom listener class needs to be defined.


136     /**
137      * Internal click listener class. Translates a view’s click listener to
138      * one that is more appropriate for the custom image button class.
139      *
140      * @author Kah
141      */
142     private class InternalListener implements View.OnClickListener
143     {
144         private ClickListener listener = null;
145    
146         /**
147          * Changes the listener to the given listener.
148          *
149          * @param newListener
150          *        The listener to change to.
151          */
152         public void setListener(ClickListener newListener)
153         {
154             listener = newListener;
155         }
156        
157         @Override
158         public void onClick(View v) 
159         {
160             if (listener != null)
161             {
162                 listener.onClick(CustomImageButton.this);
163             }
164         }
165     }
166    }
167    

+ Recent posts