Android Custom View (BUTTONBUZZER)
XML Custom View

Android Custom View (BUTTONBUZZER)

XML Custom View

Overview Custom Views

Android UI elements are all based on View (single element on screen) and ViewGroup (collection of elements on screen).

There are many widgets and layouts built-in that can be used to build the UI such as views like Button ,TextView , and layouts like RelativeLayout . 

Custom views are typically created to provide the user interface an experience which is not possible with the default views (button, and text view, etc.), Using custom view allows the developer to do certain performance optimization, i.e., in case of a custom layout the development can optimize the layout manager for his use case. 

The android developer wants to build a new innovative and cool interaction or UI, and he cannot make it work with the current Android components: maybe the android developer need a customized view. 

There are also a few reasons why you might not want to use custom views. 

They are time-consuming, difficult in some time and need special skills. 

Customizing your own views involves extending View or an existing subclass, overriding the view behavior by writing methods such as onDraw or onTouchEvent and then using your new view in an activity.

Creating Full Custom View

Creating custom view is centered around five primary aspects that we may need to control or modify :-

  1. - Drawing Control the rendering of the view on screen visually by overriding the onDraw method. 
  2. - Interaction Control the ways the user can interact with the view with the onTouchEvent and gestures.
  3. - Measurement Control the content dimensions of the view on screen by overriding the onMeasure method.
  4. - Attributes Defining custom XML attributes for your view and using them to control behavior with TypedArray
  5. - Persistence Storing and restoring state on configuration changes to avoid losing the state with onSaveInstanceState and onRestoreInstanceState.

Show ButtonBuzzer

To take a closer look, suppose we want to create our own view control that allows the user checked on Buzzer. 

The view will display a ButtonBuzzer checked or No.

To create our own Custom View ButtonBuzzer, we start by defining a class ButtonBuzzer which extends from CompundButton and implements the required constructor:-

 public class ButtonBuzzer extends CompoundButton {

        // We must provide a constructor that takes a Context and an AttributeSet.// This constructor allows the UI to create and edit an instance of your view.
        public ButtonBuzzer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
} 

Define Custom Attributes

Well-written custom views can be configured and styled via XML attributes. You need to ask yourself which aspects of your view should be customizable. 

For example, we might want to let the user select the color of the Buzzer as well as give the user the option to change the Color of Stroke Buzzer. 

We might want the view to be configurable in XML. In order to be able to define BuzzerColor and Stroke_BuzzerColor , we need to define these as attributes within res/values/attrs.xml .

<?xml="1.0"   encoding ="utf-8"? >
  <resources>
    
< declare-styleable name="ButtonBuzzer" >
< attr name="buzzerColor" format  ="color" / >
< attr name="stroke_buzzerColor" format  ="color" / >
</<declare-styleable >

  </resources>

Notice we define the attr node along with the name and format for each custom attribute we'd like to be able to define.

The format is the expected type of value for that property and valid options include string, color, dimension, boolean, integer, float, enum, and several others.

Once you define the custom attributes, you can use them in layout XML files just like built-in attributes. 

The only difference is that your custom attributes belong to a different name-space. 

You can define the namespace within the root view of the layout and configure the properties for the view.

Normally you would need to specify a namespace such as http://schemas.android.com/apk/res/ (i.e.com.AndroidCustomView.example.customviewTemp) but the name-space. 

xmlns:app=”http://schemas.android.com/apk/res-auto” will auto-resolve for you.

Apply Custom Attributes

Now that we have set custom properties such as BuzzerColor and Stroke_BuzzerColor, we need to extract those properties to be used within our custom View within the constructor. 

To extract the custom attributes, we can use a TypedArray and the obtainStyledAttributes on the AttributeSet :-

  public class ButtonBuzzer extends CompoundButton {

    private int buzzerColor;
    private int stroke_buzzerColor;

    public ButtonBuzzer(Context context, AttributeSet attrs) {
       super (context, attrs);
        GetAttributes(attrs);
    }

    private void GetAttributes(AttributeSet attrs) {
        // Obtain a typed array of attributes    

        TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable. ButtonBuzzer, 0, 0  );

        // Extract custom attributes into member variables   try  {
           buzzerColor = a.getColor(R.styleable.ButtonBuzzer_buzzerColor, Color.RED);
           stroke_buzzerColor = a.getColor(R.styleable.ButtonBuzzer_stroke_buzzerColor, Color. GRAY   );
        }  finally   {
            // TypedArray objects are shared and must be recycled.  
            a.recycle();
        }
    }
}

Add Property Methods

Let's expose property methods to allow us to get and set the important properties after a view has been created in runtime Apps :

 public class ButtonBuzzer extends CompoundButton {

     private int buzzerColor;
     private int stroke_buzzerColor;
    
     public ButtonBuzzer(Context context, AttributeSet attrs) {...}
     private void GetAttributes(AttributeSet attrs) {...}

     public int getStroke_buzzerColor() {
         return stroke_buzzerColor;
    }

     public void  setStroke_buzzerColor( int stroke_buzzerColor) {
         this.stroke_buzzerColor = stroke_buzzerColor;
        invalidate();
        requestLayout();
    }

    public int getBuzzerColor() {
        return buzzerColor;
    }

    public void setBuzzerColor( int buzzerColor) {
        this.buzzerColor = buzzerColor;
        invalidate();
        requestLayout();
    }
}

Notice that when the view properties are changed and might require a redraw, be sure to call invalidate() and requestLayout() to update the appearance.

Calculating the Dimensions

In order to understand the width and height of a view or other Dimensions attributes that is being custom drawn, 

we need to define the onMeasure method which determines the width and height of the view . 

Let's define the onMeasure as follows:-

 public class ButtonBuzzer extends CompoundButton {

   private int  buzzerColor;
   private int  stroke_buzzerColor;

   float  width;
   float  height;

   public  ButtonBuzzer(Context context, AttributeSet attrs) {...}
   private void  GetAttributes(AttributeSet attrs) {...}
   public int  getStroke_buzzerColor() {...}
   public void  setStroke_buzzerColor( int stroke_buzzerColor) {...}
   public int  getBuzzerColor() {...}
   public void  setBuzzerColor( int buzzerColor) {...}
    
   @Override   
   protected void  onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        width =  this.getWidth();
        height =  this.getHeight();
    }     

}

Drawing ButtonBuzzer

Next, let's actually draw a ButtonBuzzer taking into account the properties defined above for Buzzer Color and Stroke Color . 

All view drawing happens within the onDraw() method using the Canvas object to paint onto the view. Now need execute Override on method onDraw() This will paint the Buzzer based on the BuzzerColor and Stroke_BuzzerColor specified in the XML.

public class ButtonBuzzer extends CompoundButton {

    private int  buzzerColor;
    private int  stroke_buzzerColor;
    float  width;
    float  height;

    private  Paint paint;
    private  Path path;
    public  ButtonBuzzer(Context context, AttributeSet attrs) {
    super (context, attrs);
    GetAttributes(attrs);

    paint =  new  Paint(Paint.ANTI_ALIAS_FLAG);
    path =  new  Path();
    }

     private void  GetAttributes(AttributeSet attrs) {...}
     public int  getStroke_buzzerColor() {...}
     public void  setStroke_buzzerColor(int stroke_buzzerColor) {...}
     public int  getBuzzerColor() {...}
     public void  setBuzzerColor(int buzzerColor) {...}
     @Override  
     protected void  onMeasure( int widthMeasureSpec, int heightMeasureSpec){...}
    
    @Override 
     protected void onDraw(Canvas canvas) {
     super .onDraw(canvas);

        width =  this.getWidth();
        height =  this.getHeight();


        float  space = (Math.min(width , height)/ 10  );

        float  TL2x = (width/10  ) + space ;
        float  TL2y = (height/10  ) - space;

        float TL1x = (width/10  ) - space ;
        float  TL1y = (height/10  ) + space;

        float BL1x = TL1x ;
        float  BL1y = (height - (height/10  )) - space;

        float BL2x = TL2x ;
        float BL2y = (height - (height/10  )) + space;

         float  BR1x = (width - (width/10  )) - space ;
         float  BR1y = BL2y;

         float  BR2x = (width - (width/10  )) + space ;
         float  BR2y = BL1y;

      
         float  TR1x = BR2x;
         float  TR1y = TL1y;

         float  TR2x = BR1x;
         float  TR2y = TL2y;

        path.moveTo(TL2x , TL2y);
        path.lineTo(TL1x , TL1y);
        path.lineTo( (width/ 2  ) - space , height/ 2  );
        path.lineTo(BL1x , BL1y);
        path.lineTo(BL2x , BL2y);
        path.lineTo(width/ 2   , (height/ 2  ) + space);
        path.lineTo(BR1x , BR1y);
        path.lineTo(BR2x , BR2y);
        path.lineTo((width/ 2  )+ space , height/ 2  );
        path.lineTo( (width/ 2  )+ space , height/ 2  );
        path.lineTo(TR1x , TR1y);
        path.lineTo(TR2x , TR2y);
        path.lineTo(width/ 2   , (height/ 2  ) - space);
        path.lineTo(TL2x , TL2y);

      
        paint.setColor(buzzerColor);

        if (this.isChecked()){
            paint.setStyle(Paint.Style.FILL);
            canvas.drawPath(path, paint);
        }

        paint.setColor(stroke_buzzerColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(space/ 3  );
        canvas.drawPath(path, paint);
    }
}

Apply Custom Event

This will paint the Buzzer based on the BuzzerColor and Stroke_BuzzerColor specified in the XML.

We have the Buzzer drawing, but we want the BuzzerColor to toggle each time the view is clicked.

Let's setup a touch handler to ensure the BuzzerColor changes as specified using the onTouchEvent method:

 public class ButtonBuzzer extends CompoundButton {

     private int  buzzerColor;
     private int  stroke_buzzerColor;
     float  width;
     float  height;


     private  Paint paint;
     private  Path path;
     public  ButtonBuzzer(Context context, AttributeSet attrs) {...}
     private void  GetAttributes(AttributeSet attrs) {...}
     public int  getStroke_buzzerColor() {...}
     public void setStroke_buzzerColor( int stroke_buzzerColor) {...}
     public int  getBuzzerColor() {...}
     public void  setBuzzerColor( int buzzerColor) {...}
     @Override  
     protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec){...}
     @Override  
     protected void  onDraw(Canvas canvas) {...}

     @Override  
     public boolean onTouchEvent(MotionEvent event) {

     if ( event.getAction()== MotionEvent.ACTION_UP)
        this.setChecked(( this .isChecked())?  false  : true );

         invalidate();
         requestLayout();

     return  true;
     }

}

Now whenever the ButtonBuzzer is clicked, the selected Buzzer will change and a different State should be drawn after postInvalidate is called. 

Let's update the onDraw method to paint the correct ButtonBuzzer

For more advanced view user interaction, check out the Making the View Interactive official docs.

Add ButtonBuzzer in Layout

The final touch might be to add ButtonBuzzer View in Layout and can change default Value of Attributes BuzzerColor and Stroke_BuzzerColor by :

 < com.example.android_custom_view.ButtonBuzzer
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:id="@+id/ButtonBuz1"
    app:buzzerColor="#646464"
    app:stroke_buzzerColor="#FF4081"
    />

Add Action In Activity

then now within the activity, we might be able to display the selected State with a toast when a ButtonBuzzer is pressed

public class Activity_ButtonBuzzer extends AppCompatActivity {


      private  ButtonBuzzer BT;
      @Override  
      protected void  onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_button_buzz);

        BT = (ButtonBuzzer)findViewById(R.id.ButtonBuz1);
        BT.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
      @Override  
      public void  onCheckedChanged(CompoundButton compoundButton, boolean b) {
             if (BT.isChecked())
                 Toast.makeText( getBaseContext(), "The state is Yes Checked Buzzer", Toast.LENGTH_SHORT).show() ;
             else   
                 Toast.makeText(getBaseContext(), "The state is NO Checked Buzzer", Toast.LENGTH_SHORT).show()  ;
            }
        });
    }
}
     

Also, you can see Tutorial Custom View( ButtonBuzzer)

Project On GitHub

https://github.com/xmlcustomview/CustomJava/blob/master/CustomViewBuzzer.rar

References

•  http://developer.android.com/guide/topics/ui/custom-components.html  
•  http://developer.android.com/training/custom-views/index.html
•  http://www.vogella.com/articles/AndroidCustomViews/article.html
•  http://guides.codepath.com/android/defining-custom-views    
•  https://www.101apps.co.za/articles/creating-custom-views.html 
      

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics