It’s known that many of the android apps that have installed on the phone contain widgets. The most common examples are the Calendar and the Clock widget. So what are these Widgets? Widgets are just a mini-app that lives on the home screen and apart from small launcher icons that normally appear on the home screen. Widgets do take up more space and display up-to-date information of the app. Some of them are user resizable too. The most common use case of the widget is to launch the application or some specific activity of it.
A widget is basically a broadcast message that communicates with the app using a receiver so you also have to include it into your Manifest File. This seems to be a lot of work, thanks to Android Studio it can do that all for us. So, just go to Android Studio click on app->New->Widget->AppWidget. Give it a name and you are done with setting up the widget. You can even check it that after installation will be having its own widget with a Simple TextView.
Now, after a brief explanation of what a widget is, let’s deep dive into how to create it. There are three steps to set up a widget of your app.
- Step 1: A WidgetProviderClass say, MyWidget, that extends the AppWidgetProvider class.
- Step 2: A WidgetProviderInfo which is an XML that describes the Widget Metadata including information such as min-height and width.
- Step 3: A widget layout file that will describe how your widget looks, but it has limitations from other layout files.
MainActivity File
Now let’s come creating our own Custom Widget which requires coding now. Create a MainActivity.xml file in the Layout folder of res directory in the following manner containing a Listview.
XML
<--!MainActivity.xml--> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > <!--List view to display all food items--> < ListView android:layout_width = "match_parent" android:layout_height = "match_parent" android:id = "@+id/recipes" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
- Now create a java file initializing all views and setting up the Listview, also populating it with items.
- Name the Java file as MainActivity.Java.
Java
import android.appwidget.AppWidgetManager; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.util.ArrayList; public class DetailsActivity extends AppCompatActivity { TextView name, content; ImageView addToWidget; boolean added = false ; // Take your steps of food processing as String variables // recipe1 and recipe2. private String recipe1 = "Step1: Take a Lemon and required no of glasses of water" + "Step2: Squeeze out the lemon juice into glasses,stir well" + "and put iceCubes before serve" ; private String recipe2 = "Step1: Take a bread and apply some butter on it" + "Step2:Put it in the toaster and it is ready" ; ArrayList<String> steps = new ArrayList<String>(); public static Recipe recipe; AppWidgetManager appWidgetManager; int appWidgetId; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); // attach xml file for detailsActivity,that will // show detail of every food item setContentView(R.layout.activity_details); // Add Steps into step ArrayList steps.add(recipe1); steps.add(recipe2); addToWidget = findViewById(R.id.addToWidget); // AppWidgetManager manages creating and updating // the multiple widgets an application can have. appWidgetManager = AppWidgetManager.getInstance( DetailsActivity. this ); appWidgetId = 1 ; // Each AppWidget has a different appWidgetId to // make it unique. name = findViewById(R.id.name); content = findViewById(R.id.steps); final String heading = getIntent().getStringExtra( "name" ); final int pos = getIntent().getIntExtra( "pos" , - 1 ); recipe = new Recipe(heading, steps.get(pos)); name.setText(heading); content.setText(steps.get(pos)); // Attach clickListener on ImageView Object so when // we will click it will handle the widget adding // code. addToWidget.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { added = !added; // boolean variable to // know the state ,if // widget is added or not. Toast .makeText(DetailsActivity. this , "Click" , Toast.LENGTH_SHORT) .show(); if (added) { // Calling updateAppWidget static // method of RecipeWidget to update // widgets of app RecipeWidget.updateAppWidget( DetailsActivity. this , appWidgetManager, appWidgetId, recipe); Toast .makeText(DetailsActivity. this , "Added to Widget" , Toast.LENGTH_SHORT) .show(); addToWidget.setImageDrawable( getResources().getDrawable( R.drawable.add_widget)); } else { addToWidget.setImageDrawable( getResources().getDrawable( R.drawable.not_widget)); RecipeWidget.updateAppWidget( DetailsActivity. this , appWidgetManager, appWidgetId, null ); } } }); } // This method was created to pass Recipe object // information to AppWidget. public static Recipe getRecipe() { return recipe; } } |
Output:
DetailsActivity File
Now, we have added two items to our list of recipes and each item contains a clicklistener attached that would navigate to DetailsActivity.java with intended information passing through intent. The following is the layout for DetailsActivity.xml containing two textview and a AddWidget Button(ImageView).
XML
<--!DetailsActivity.xml> <? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".DetailsActivity" > <!--A Textview to display name of food--> < TextView android:id = "@+id/name" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "16dp" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintHorizontal_bias = "0.0" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> <!--A ImageView to let user add a widget by clicking on it --> < ImageView android:id = "@+id/addToWidget" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:background = "@android:color/white" android:clickable = "true" android:contentDescription = "@string/addtowidget" android:focusable = "true" android:src = "@drawable/not_widget" app:layout_constraintBottom_toTopOf = "@+id/steps" app:layout_constraintStart_toStartOf = "parent" /> <!--A TextView to show steps --> < TextView android:id = "@+id/steps" android:layout_width = "match_parent" android:layout_height = "wrap_content" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintHorizontal_bias = "0.0" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toBottomOf = "@+id/name" app:layout_constraintVertical_bias = "0.158" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
- Create an instance of AppWidgetManager object.
- Give it an id,i.e AppWidgetId, to make it unique.
- Use the app->new->vector asset->select a drawable that you want your image to display like we have chosen star vector(R.drawable.addToWidget).
Java
import android.appwidget.AppWidgetManager; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.util.ArrayList; public class DetailsActivity extends AppCompatActivity { TextView name, content; ImageView addToWidget; boolean added = false ; // Take your steps of food processing as String variables // recipe1 and recipe2. private String recipe1 = "Step1: Take a Lemon and required no of glasses of water" + "Step2: Squeeze out the lemon juice into glasses,stir well" + "and put iceCubes before serve" ; private String recipe2 = "Step1: Take a bread and apply some butter on it" + "Step2:Put it in the toaster and it is ready" ; ArrayList<String> steps = new ArrayList<String>(); public static Recipe recipe; AppWidgetManager appWidgetManager; int appWidgetId; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); // attach xml file for detailsActivity,that will // show detail of every food item setContentView(R.layout.activity_details); // Add Steps into step ArrayList steps.add(recipe1); steps.add(recipe2); addToWidget = findViewById(R.id.addToWidget); // AppWidgetManager manages creating and updating // the multiple widgets an application can have. appWidgetManager = AppWidgetManager.getInstance( DetailsActivity. this ); appWidgetId = 1 ; // Each AppWidget has a different appWidgetId to // make it unique. name = findViewById(R.id.name); content = findViewById(R.id.steps); final String heading = getIntent().getStringExtra( "name" ); final int pos = getIntent().getIntExtra( "pos" , - 1 ); recipe = new Recipe(heading, steps.get(pos)); name.setText(heading); content.setText(steps.get(pos)); // Attach clickListener on ImageView Object so when // we will click it will handle the widget adding // code. addToWidget.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { added = !added; // boolean variable to // know the state ,if // widget is added or not. Toast .makeText(DetailsActivity. this , "Click" , Toast.LENGTH_SHORT) .show(); if (added) { // Calling updateAppWidget static // method of RecipeWidget to update // widgets of app RecipeWidget.updateAppWidget( DetailsActivity. this , appWidgetManager, appWidgetId, recipe); Toast .makeText(DetailsActivity. this , "Added to Widget" , Toast.LENGTH_SHORT) .show(); addToWidget.setImageDrawable( getResources().getDrawable( R.drawable.add_widget)); } else { addToWidget.setImageDrawable( getResources().getDrawable( R.drawable.not_widget)); RecipeWidget.updateAppWidget( DetailsActivity. this , appWidgetManager, appWidgetId, null ); } } }); } // This method was created to pass Recipe object // information to AppWidget. public static Recipe getRecipe() { return recipe; } } |
Output:
Recipe.Java File
Now, each Recipe contains a Recipe name(String) and a Recipe Steps(String) so for our ease of maintaining we will create a Recipe object.
Java
package com.tanya.widgettutorial; public class Recipe { // The recipe include name of food item and steps to // cook it. private String name; private String steps; public Recipe(String name, String steps) { this .name = name; this .steps = steps; } // Getters and Setters public String getName() { return name; } public void setName(String name) { this .name = name; } public String getSteps() { return steps; } |
Three words you must know about a widget:
- RemoteView: Now, previously I mentioned that widget layout is the same as any other layout. So, basically, Widget Layout is based on RemoteView because they are treated as a separate app on your homescreen. RemoteView is used to describe a view hierarchy that will be displayed in another process.
- onUpdate() method: The onUpdate() method is called when a new Widget is created and also during each update interval which is set in the Widgetinfo.xml file,i.e generated when you have created Widget for your app under the xml directory.
- AppWidgetManager class: This class consists of all the information about the widgets that are present at home. It also provides access t force updates on all existing widgets.
RecipeWidget.Java
Java
package com.tanya.widgettutorial; public class Recipe { // The recipe include name of food item and steps to // cook it. private String name; private String steps; public Recipe(String name, String steps) { this .name = name; this .steps = steps; } // Getters and Setters public String getName() { return name; } public void setName(String name) { this .name = name; } public String getSteps() { return steps; } |
Output: