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 :-
- - Drawing Control the rendering of the view on screen visually by overriding the onDraw method.
- - Interaction Control the ways the user can interact with the view with the onTouchEvent and gestures.
- - Measurement Control the content dimensions of the view on screen by overriding the onMeasure method.
- - Attributes Defining custom XML attributes for your view and using them to control behavior with TypedArray.
- - 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