Monday 22 September 2014

Handling Orientation Changes and dismissal of Alert dialog


An alert dialog is basically used for displaying an alert to the user. It can also be used for displaying a custom view to the user. The most important difficulty developers face is that of orientation changes i.e when a device is rotated i.e. from portrait to landscape or landscape to portrait, then the alert dialog is dismissed. Sometimes it may also happens that the alert dialog window during orientation changes may leak memory. So, in order to prevent all these issues from making your app unstable, we need to handle it. I will show you how to do it.

First of all i will post the whole code, and then i will explain how to handle configuration changes and memory leak in alert dialog.

activity_c.xml


    <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:id="@+id/tbllyt"
                 android:layout_width="match_parent"
                 android:layout_height="300dp"
                 android:layout_centerInParent="true"
                 android:stretchColumns="0,1"
                 android:shrinkColumns="2"
                 android:collapseColumns="3"
                 android:layout_span="2"
                 android:background="@color/tbllytbckgrnd" >
     
       <TableRow >
           <Button android:id="@+id/btn"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:text="BUTTON"
                   android:textSize="25sp"/> 
            <Button android:id="@+id/alertbtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="ALERT" 
                    android:textSize="25sp"/>        
       </TableRow>
    </TableLayout>

ActivityC.java
package com.anroid.androprac;

import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


public class ActivityC extends Activity  {

Context context = this;
TextView tv;
int checkedItem;
String selectedItem;
int configChangeFlag=0;
AlertDialog alertDialog;
CharSequence [] countries;
boolean [] selectedCountries;
ArrayList<String> checkedCountries = new ArrayList<String>();
boolean submitBtnClicked, isDialogCancelled;
Button alertbtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
countries = getResources().getStringArray(R.array.Countries);

if(savedInstanceState != null){
configChangeFlag = savedInstanceState.getInt("configChangeFlag");
selectedCountries = savedInstanceState.getBooleanArray("checkedCountries");
submitBtnClicked = savedInstanceState.getBoolean("submitBtnClicked");
isDialogCancelled = savedInstanceState.getBoolean("isDialogCancelled");
} else {
selectedCountries = new boolean[countries.length];
}
if(configChangeFlag==1 && submitBtnClicked == false && isDialogCancelled == false){
invokeAlert();
 } 
Button tb = (Button)findViewById(R.id.btn);
tb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(ActivityC.this, DynamicCheckboxes.class);
startActivity(intent);
}
});
alertbtn = (Button)findViewById(R.id.alertbtn);
alertbtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
invokeAlert();

}
});
}
    
public void invokeAlert(){
AlertDialog.Builder builder = new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_LIGHT);
builder.setTitle("Choose Countries");
builder.setMultiChoiceItems(R.array.Countries, selectedCountries , new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if(isChecked == true){
selectedCountries[which] = isChecked;
}
Toast.makeText(context, countries[which], Toast.LENGTH_SHORT).show();
}
}).setCancelable(true);
builder.setPositiveButton("SUBMIT", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
submitBtnClicked = true;
final Toast displaySelectedCountry = Toast.makeText(context, "", Toast.LENGTH_SHORT);
for(int i=0;i<selectedCountries.length;i++){
if(selectedCountries[i]==true){
checkedCountries.add(countries[i]+"");
}
}
 for(CharSequence checkedCountry : checkedCountries){
 System.out.println(checkedCountry);  
 displaySelectedCountry.setText(checkedCountry);
 displaySelectedCountry.show();
 }
}
}).setCancelable(true);
submitBtnClicked = false;
builder.setInverseBackgroundForced(true);
alertDialog = builder.create();
alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
  if(submitBtnClicked)
    Arrays.fill(selectedCountries, false);
  else
isDialogCancelled = true;
}
});
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
  isDialogCancelled = false;
}
});
alertDialog.show();
}
public void onSaveInstanceState(Bundle bundle){
super.onSaveInstanceState(bundle);
configChangeFlag = 1;
bundle.putInt("configChangeFlag", configChangeFlag);
bundle.putBooleanArray("checkedCountries", selectedCountries);
bundle.putBoolean("submitBtnClicked", submitBtnClicked);
bundle.putBoolean("isDialogCancelled", isDialogCancelled);
}

public void onPause(){
super.onPause();
if(alertDialog!=null)
  alertDialog.dismiss();
}
}

The first thing we need to handle is memory leak in alert dialog.
We can handle it by overriding onPause callback method of the activity life cycle. What we do is we check whether the alert dialog object is null or not and then we dismiss the alert dialog if the alert dialog object is not null. The code for doing this is :--

public void onPause(){
super.onPause();
if(alertDialog!=null)
   alertDialog.dismiss();
}

The 2nd thing we need to handle is configuration changes due to change in orientation. In this code we are displaying a list of countries with checkbox. Now if we have checked some countries 
and rotate the device and if we have not handled the orientation changes then the countries you have checked are lost. But not to worry , we can save the changes we have made in the onSaveInstanceState method and can retrieve it in the onCreate callback method.
public void onSaveInstanceState(Bundle bundle){
super.onSaveInstanceState(bundle);
configChangeFlag = 1;
bundle.putInt("configChangeFlag", configChangeFlag);
bundle.putBooleanArray("checkedCountries", selectedCountries);
bundle.putBoolean("submitBtnClicked", submitBtnClicked);
bundle.putBoolean("isDialogCancelled", isDialogCancelled);
}
In the above method we are saving the state of the alert dialog i.e the array of countries checked  and a variable that helps us to determine whether orientation change has occurred or not in a bundle and the other things that we are saving i will explain it later.  

We can retrieve the saved changes in onCreate method by

if(savedInstanceState != null){
configChangeFlag = savedInstanceState.getInt("configChangeFlag");
selectedCountries = savedInstanceState.getBooleanArray("checkedCountries");
submitBtnClicked = savedInstanceState.getBoolean("submitBtnClicked");
isDialogCancelled = savedInstanceState.getBoolean("isDialogCancelled");
} else {
selectedCountries = new boolean[countries.length];
}

Now if the submit button is clicked and the alert dialog is dismissed then during changes in orientation the alert dialog shouldn't be displayed. This is done by setting the variable submitBtnClicked to true in on onClick method of  builder.setPositiveButton and saving it's state in onSaveInstanceState. Now if the ALERT dialog is dismissed by clicking on submit button of alert dialog then the alert dialog will not be displayed during orientation changes. But if the alert dialog is dismissed by clicking the back button then also the alert dialog is not displayed during configuration changes but the countries that are checked are not lost, and if we click on ALERT button then the alert dialog is displayed with the previously checked countries.

builder.setPositiveButton("SUBMIT", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
submitBtnClicked = true;
final Toast displaySelectedCountry = Toast.makeText(context, "", Toast.LENGTH_SHORT);
for(int i=0;i<selectedCountries.length;i++){
if(selectedCountries[i]==true){
checkedCountries.add(countries[i]+"");
}
}
  for(CharSequence checkedCountry : checkedCountries){
  System.out.println(checkedCountry);  
  displaySelectedCountry.setText(checkedCountry);
  displaySelectedCountry.show();
  }
}
}).setCancelable(true);
submitBtnClicked = false;
builder.setInverseBackgroundForced(true);
alertDialog = builder.create();
alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
   if(submitBtnClicked)
     Arrays.fill(selectedCountries, false);
   else
isDialogCancelled = true;
}
});
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
   isDialogCancelled = false;
}
});
alertDialog.show();
}
Finally the alert dialog is displayed during orientation changes only if the orientation changes have happened and alert dialog is not dismissed due to the clicking of the submit button of alert dialog and finally if alert dialog is not dismissed due to the clicking of the back button.  These conditions are checked by using the following code:--

if(configChangeFlag==1 && submitBtnClicked == false && isDialogCancelled == false){
invokeAlert();
  }

Output:--

In Portrait Mode:--

In landscape mode



So this is all about how to handle the orientation changes and dismissal of alert dialog without loosing data.

Happy Coding :)

Friday 12 September 2014

Creating Dynamic Checkbox in Android

Creating Dynamic Checkbox in Android

In order to create dynamic checkbox in Android, the following steps are to be followed:--

1. The checkboxes that are to be created dynamically depends on the number of data items which may come from a remote server or your own local database. In this situation i have used a string array resource which contains a array of countries. The resource file is contained in the values sub directory  of the resources directory. This string array is defined in the file strings.xml.

    <string-array name="Countries">
        <item>India</item>
        <item>Pakistan</item>
        <item>US</item>
        <item>UK</item>
        <item>Switzerland</item>
        <item>Spain</item>
        <item>France</item>
        <item>Germany</item>
        <item>Finland</item>
        <item>China</item>
        <item>Japan</item>
        <item>Thailand</item>
        <item>Singapore</item>
        <item>South Africa</item>
        <item>Russia</item>
    </string-array>

2. The next step is to create a layout file. You can see that ScrollView is used , it is basically used in order to scroll vertically through the list of checkboxes.
activity_dynamic_checkboxes.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".DynamicCheckboxes" >

    <LinearLayout android:id="@+id/childlyt"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="vertical">
        
        <Button android:id="@+id/saveProceed"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/saveProceed"
                android:layout_gravity="center_horizontal"
                android:background="@drawable/custombutton"/>
        
              
    </LinearLayout>"
    <ScrollView android:id="@+id/scrlvw"
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:layout_below="@+id/childlyt"
                android:fillViewport="true"
                android:layout_centerHorizontal="true"
                android:paddingTop="9dp"
                >
    <LinearLayout android:id="@+id/chkboxlyt"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:orientation="vertical">
                  
       <View android:id="@+id/customline"
             android:layout_width="fill_parent"
             android:layout_height="5dp"
             android:background="@color/textcolr"/> 
        
    </LinearLayout>
  </ScrollView>  
</RelativeLayout>

3. Finally the code :--
   3.1 Code for dynamic creation of checkbox. What happens in this code is that first of all we obtain the array of countries from the string array resources. Then a array is created in order to hold the checkboxes created dynamically/programmatically.
String [] countries_array = getResources().getStringArray(R.array.Countries);


CheckBox[] dynamicCheckBoxes = new CheckBox[countries_array.length];

After the creation of the array of dynamic checkboxes and retrieving from the string array resources, the array of countries, the id of the layout in which the checkboxes is to be displayed is obtained and then in the for loop the checkboxes are created dynamically and it's properties are set. Then the checkbox i.e. created dynamically is added to the layout. In order to handle the events i.e. check and uncheck related to checkbox, the listener for a checkbox is set i.e. 


cb.setOnCheckedChangeListener(this);

For the above statement to work properly and not throw compiler error you have to implement i.e your activity must implement CompoundButton.OnCheckedChangeListener interface.

LinearLayout checkboxLayout = (LinearLayout)findViewById(R.id.chkboxlyt);
for(int i=0;i<dynamicCheckBoxes.length;i++){
CheckBox cb = new CheckBox(this);
cb.setText(countries_array[i]);
cb.setTextSize(27);
cb.setTextColor(Color.rgb(150, 190, 200));
cb.setTypeface(Typeface.MONOSPACE);
cb.setButtonDrawable(R.drawable.checkboxselector);

dynamicCheckBoxes[i]=cb;
checkboxLayout.addView(cb);
cb.setOnCheckedChangeListener(this);


}

The callback method implementation for  setOnCheckedChangeListener of the checkbox

public void onCheckedChanged(CompoundButton cb, boolean isChecked){
String checkedText = cb.getText()+"";

if(isChecked){
checkedCountries.add(checkedText);
Toast.makeText(this, cb.getText()+" is checked!!!", Toast.LENGTH_SHORT).show();
} else {
checkedCountries.remove(checkedText);
Toast.makeText(this, cb.getText()+" is not checked!!!", Toast.LENGTH_SHORT).show();
}


}

In this callback method onCheckedChanged the state of the checkbox is determined i.e if the checkbox is checked or not. If the checkbox is checked then the text related to the checkbox i.e. checked is obtained and added to the ArrayList and then the country name i.e. checked is displayed using Toast. If the checkbox previously checked is unchecked then it is removed from the list. A toast is displayed i.e. the name of the checkbox i.e. unchecked.

4. Handling the configuration changes. In this the configuration change that may happen is orientation change from portrait to landscape. So when there is a change in orientation the checked checkboxes must not be lost i.e. the checkboxes that were checked in portrait mode must be retained in landscape orientation.

So in order to handle successfully orientation changes override the onSaveInstanceState callback method.
In this method initialize the flag to 1 and save the checkedCountries ArrayList and the flag in the bundle.

public void onSaveInstanceState(Bundle savedState){
super.onSaveInstanceState(savedState);
flag=1;
savedState.putStringArrayList("checkedCountries", checkedCountries);
savedState.putInt("savedflag", flag);
}


}

Now to restore the saved state in the new configuration do the following thing:--
Define a ArrayList of checked countries and then obtain from the bundle the arraylist of checked countries and the flag which determine whether orientation has taken place or not. 
Check if the flag is 1 then loop through the array of dynamic checkboxes and then set the checkboxes as checked which were previously checked by using the toggle button of the checkbox.

int flag=0;
ArrayList <String>retrievedCheckedCountriesList;

if(savedInstanceState!=null){
retrievedCheckedCountriesList = savedInstanceState.getStringArrayList("checkedCountries");
flag = savedInstanceState.getInt("savedflag");
}
String [] countries_array = getResources().getStringArray(R.array.Countries);
CheckBox[] dynamicCheckBoxes = new CheckBox[countries_array.length];
LinearLayout checkboxLayout = (LinearLayout)findViewById(R.id.chkboxlyt);
for(int i=0;i<dynamicCheckBoxes.length;i++){
CheckBox cb = new CheckBox(this);
cb.setText(countries_array[i]);
cb.setTextSize(27);
cb.setTextColor(Color.rgb(150, 190, 200));
cb.setTypeface(Typeface.MONOSPACE);
cb.setButtonDrawable(R.drawable.checkboxselector);

dynamicCheckBoxes[i]=cb;
checkboxLayout.addView(cb);
cb.setOnCheckedChangeListener(this);
}
if(flag!=0){
for(CheckBox cb:dynamicCheckBoxes){
for(int i=0; i<retrievedCheckedCountriesList.size();i++){
if((cb.getText()+"").equals(retrievedCheckedCountriesList.get(i))){
cb.toggle();
}
}
 }
}


}

DynamicCheckboxes.java
package com.anroid.androprac;

import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.Toast;

public class DynamicCheckboxes extends Activity implements CompoundButton.OnCheckedChangeListener {
int flag=0;
ArrayList <String>checkedCountries = new ArrayList<String>();
ArrayList <String>retrievedCheckedCountriesList;
Button saveProceed;
@Override
protected void onCreate(Bundle savedInstanceState)  {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_checkboxes);
if(savedInstanceState!=null){
retrievedCheckedCountriesList = savedInstanceState.getStringArrayList("checkedCountries");
flag = savedInstanceState.getInt("savedflag");
}
saveProceed = (Button)findViewById(R.id.saveProceed);
saveProceed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(DynamicCheckboxes.this, ActivityD.class);
intent.putStringArrayListExtra("checkedVals", checkedCountries);
startActivity(intent);
}
});
String [] countries_array = getResources().getStringArray(R.array.Countries);
CheckBox[] dynamicCheckBoxes = new CheckBox[countries_array.length];
LinearLayout checkboxLayout = (LinearLayout)findViewById(R.id.chkboxlyt);
for(int i=0;i<dynamicCheckBoxes.length;i++){
CheckBox cb = new CheckBox(this);
cb.setText(countries_array[i]);
cb.setTextSize(27);
cb.setTextColor(Color.rgb(150, 190, 200));
cb.setTypeface(Typeface.MONOSPACE);
cb.setButtonDrawable(R.drawable.checkboxselector);

dynamicCheckBoxes[i]=cb;
checkboxLayout.addView(cb);
cb.setOnCheckedChangeListener(this);
}
if(flag!=0){
for(CheckBox cb:dynamicCheckBoxes){
for(int i=0; i<retrievedCheckedCountriesList.size();i++){
if((cb.getText()+"").equals(retrievedCheckedCountriesList.get(i))){
cb.toggle();
}
}
}
}
}
public void onCheckedChanged(CompoundButton cb, boolean isChecked){
String checkedText = cb.getText()+"";

if(isChecked){
checkedCountries.add(checkedText);
Toast.makeText(this, cb.getText()+" is checked!!!", Toast.LENGTH_SHORT).show();
} else {
checkedCountries.remove(checkedText);
Toast.makeText(this, cb.getText()+" is not checked!!!", Toast.LENGTH_SHORT).show();
}
}

public void onSaveInstanceState(Bundle savedState){
super.onSaveInstanceState(savedState);
flag=1;
savedState.putStringArrayList("checkedCountries", checkedCountries);
savedState.putInt("savedflag", flag);
}
}

5. The selector file which is used to select the drawable based upon the state of the checkbox.
The selector which is used for checkbox selection.
checkboxselector.xml 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_checked="true" android:drawable="@drawable/checked"/>
    <item android:state_checked="false" android:drawable="@drawable/unchecked"/>

</selector>

The Final Output :--

In Portrait Mode




In Landscape Mode






The selected countries


The drawables used in this :--
checked.png
                                            unchecked.png

Finally this concludes the Creation of dynamic checkbox Android.
Happy coding :)

Saturday 6 September 2014

Customizing Switch Android

CUSTOMIZING SWITCH

Switch is a type of toggle button. This toggle button is supported in API 14 and in later versions. It has the same functionality as defined for toggle button. The specialty of this compound button is that it provides a slider which you can slide to check/select a particular state. The button which you use to move/slide in order to select a particular state is known as thumb & the thing on which the thumb slides back and forth is known as track. So in order to customize the switch we can customize the thumb and track. We can also use selectors for selecting the appropriate drawable or color for a particular state in which the switch compound button is in.

So based on the following information, let's get started.

The first step is to customize the thumb, but the background color and text of the thumb changes according to the state the button is in. So in order to achieve this use <selector>.

customswitchselector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_checked="true">
        <shape android:shape="rectangle"
               android:visible="true"
               android:dither="true"
               android:useLevel="false">
       
        <gradient android:startColor="#66AAFF00"
                  android:endColor="#6600FF00"
                  android:angle="270"/>
    
       <corners android:radius="15dp"/>
    
       <size android:width="27dp"
          android:height="37dp" />
       </shape>
      </item>
    
    <item android:state_checked="false">
         <shape android:shape="rectangle"
               android:visible="true"
               android:dither="true"
               android:useLevel="false">
       
        <gradient android:startColor="#66FF0000"
                  android:endColor="#66FF00CC"
                  android:angle="270"/>
    
       <corners android:radius="15dp"/>
    
       <size android:width="27dp"
          android:height="37dp" />
       </shape>
    </item>


</selector>

In the above xml file the selector for changing the background of the thumb according to the state the switch button is in. As you can see, custom shapes are nested inside item elements. The nested custom shapes is selected as per the state of the switch.

Now the next step is to customize the track in which the thumb slides.

customtrack.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
       android:shape="rectangle"
       android:visible="true"
       android:dither="true"
       android:useLevel="false">
    <gradient android:startColor="#660000FF"
              android:endColor="#6600CCFF"
              android:angle="270"/>
    <corners android:radius="15dp"/>
    <size android:width="30dp"
          android:height="40dp" />
</shape>

Well the above xml file defines the custom shape for track in which the thumb slides. In android custom shapes are defined in xml. The root node is shape with an xmlns attribute that defines the namespace.
Basically 4 types of custom shapes are supported i.e. rectangle, oval, ring and line. Then after defining the shape that you want to use, specify some properties related to shape such as dither, visible and useLevel  etc.

Some more nodes or elements are also supported such as gradient, solid, corners, stroke, padding and size.
gradient node is basically used for defining gradient background. corners node is used for defining corners basically for rectangle shape. Now we can also define the size of the custom shape using the size node.

Now the layout file in which the switch button is defined:--

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@drawable/background"
              android:orientation="vertical">
              
        <include layout="@layout/customtitlebar"/>

     <Switch android:id="@+id/swtch"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textOn="@string/checkedstate"
             android:textOff="@string/uncheckedstate"
             android:minWidth="25dp"
             android:layout_marginTop="29dp"
             android:layout_marginLeft="40dp"
             android:switchPadding="10dp"
             android:onClick="togglestatehandler"
             android:thumb="@drawable/customswitchselector"
             android:track="@drawable/customtrack"
             android:thumbTextPadding="15dp"
             android:textStyle="bold|italic"
             android:typeface="sans"/>

</LinearLayout>

Now you can see in the layout file how we have referred to the custom selector we defined for thumb and track. We have done this using android:thumb for thumb and android:track for track.
             android:thumb="@drawable/customswitchselector"
             android:track="@drawable/customtrack"

The custom selectors are defined as drawable.

The strings or text used for textOn and textOff are defined in the strings.xml file.

Here is the strings.xml file present in the values directory.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="checkedstate">START</string>
    <string name="uncheckedstate">STOP</string>
</resources>

Now the java code:--
package com.example.practice;


import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.PopupWindow;
import android.widget.Switch;
import android.widget.Toast;

@SuppressLint("NewApi")
public class ActivityE extends Activity{

Switch switchbtn;
private Context context = this;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_e);
        switchbtn = (Switch)findViewById(R.id.swtch);
        applyStyle(switchbtn.getTextOn(), switchbtn.getTextOff());
        
}
public void applyStyle(CharSequence switchTxtOn, CharSequence switchTxtOff){

Spannable styleText = new SpannableString(switchTxtOn);
StyleSpan style = new StyleSpan(Typeface.BOLD);
styleText.setSpan(style, 0, switchTxtOn.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
styleText.setSpan(new ForegroundColorSpan(Color.GREEN), 0, switchTxtOn.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
switchbtn.setTextOn(styleText);
styleText = new SpannableString(switchTxtOff);
styleText.setSpan(style, 0, switchTxtOff.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
styleText.setSpan(new ForegroundColorSpan(Color.RED), 0, switchTxtOff.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
switchbtn.setTextOff(styleText);
}
public void togglestatehandler(View v){
Switch switchbtn = (Switch)v;
boolean isChecked = switchbtn.isChecked();
if(isChecked){
Toast.makeText(context, "STARTED......", Toast.LENGTH_SHORT).show();
        } else {
Toast.makeText(context, "STOPPED......", Toast.LENGTH_SHORT).show();
        }

}
}

The most noticeable thing in the code is changing the color of the text used in the thumb based on the changing of the state of the compound button i.e Switch.

 public void applyStyle(CharSequence switchTxtOn, CharSequence switchTxtOff){

Spannable styleText = new SpannableString(switchTxtOn);
StyleSpan style = new StyleSpan(Typeface.BOLD);
 styleText.setSpan(style, 0, switchTxtOn.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
 styleText.setSpan(new ForegroundColorSpan(Color.GREEN), 0, switchTxtOn.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
 switchbtn.setTextOn(styleText);
 styleText = new SpannableString(switchTxtOff);
 styleText.setSpan(style, 0, switchTxtOff.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
 styleText.setSpan(new ForegroundColorSpan(Color.RED), 0, switchTxtOff.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
 switchbtn.setTextOff(styleText);
}
Now in the above code as we know that Switch has two states and two texts are used to represent the appropriate state. For on state we used START and for off state it is STOP. Now we want to change the color of the text. So we did it using Spannable. What this code does is that it makes two spannable objects for styling the checked text and unchecked state of the Switch button. 

So , the last thing remains here is how to capture the state of the Switch button and performs appropriate action based upon the state the Switch button is in.

This is done by using togglestatehandler method . This method name is defined in the layout file as 
             android:onClick="togglestatehandler"

So now when the Switch button is clicked or the state of the compound button is changed manually then the method defined as value for the onClick attribute is invoked. It's definition is provided below:--

public void togglestatehandler(View v){
Switch switchbtn = (Switch)v;
boolean isChecked = switchbtn.isChecked();
if(isChecked){
Toast.makeText(context, "STARTED......", Toast.LENGTH_SHORT).show();
        } else {
Toast.makeText(context, "STOPPED......", Toast.LENGTH_SHORT).show();
        }
}

What this code does is that it checks the state of the Switch button and performs appropriate action based upon the state the Switch button is in. In this case we are simply displaying a Toast based upon the state of the Switch compound button.

Finally the included layout:--

customtitlebar.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent" 
          android:layout_height="70dp"
          android:background="@drawable/custom_titlebar"
          android:orientation="vertical"
          android:id="@+id/customtitle">
  
    <TextView android:id="@+id/titleText"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:paddingTop="20dp"
               android:paddingBottom="20dp"
               android:layout_gravity="center_horizontal"
               android:text="@string/title_activity_activity_b"
               android:textSize="26sp"/>     
    </LinearLayout>  

Output :--

In Start State




In Stop State



Finally we have successfully customized the switch button. 
Happy Coding :)