Monday, August 26, 2019

Create a scrollable widget for your Android app

Create an Android Widget Why We Sleep Audible Homescreen

Since the early days of the OS, widgets for Android have allowed users to engage with their favourite apps, from the comfort of their homescreen. So how do you create an Android widget?

For the developer, widgets give your application a valuable presence on the user's homescreen. Instead of being tucked out of sight in the app drawer, users will be reminded about your app every single time they glance at their homescreen – while also getting a preview of your app's most interesting and useful content.

Widgets give your application a valuable presence on the user's homescreen

In this article, I'll show you how to provide a better user experience while encouraging users to engage with your app, by creating an Android widget! By the end of this article, you'll have created a scrollable collection widget that displays a complete data set on the user's homescreen.

To ensure you're delivering the kind of widget that users want to place on their homescreen, we'll also be creating a configuration Activity, which will allow users to customize the widget's content, appearance and features. Finally, I'll show how you can encourage people to use your widget, by creating a Widget Preview image that showcases the best that your widget has to offer. 

Also read: Developing for foldable devices: What you need to know

What are widgets for Android?

An application widget is a lightweight, miniature application that lives on the user's homescreen.

Android app widgets can contain a variety of content

Widgets for Android can provide a range of content, but generally fall into one of the following categories:

  • Information widget. This is a non-scrollable widget that displays some information, such as today's weather forecast or the date and time.
  • Collection widgets. This is a scrollable widget that displays a set of related data, formatted as a ListView, GridView, StackView, or an AdapterViewFlipper. Collection widgets are usually backed by a data source, such as a database or an Array.
  • Control widgets. These widgets act as a remote control that enables users to interact with your application, without having to bring it to the foreground. Apps that play media, such as podcasts or music, often have control widgets that allow the user to trigger Play, Pause, and Skip actions directly from their homescreen.
  • Hybrid widgets. Sometimes you may be able to deliver a better user experience by combining elements from multiple categories. For example, if you're developing a control widget for a music application then you can provide Play, Pause and Skip controls, but you may also decide to display some information, such as the song's title and artist. If you do decide to mix and match, then don't get carried away! Widgets tend to deliver the best user experience when they provide easy access to a small amount of timely, relevant information or a few commonly-used features. To help keep your hybrid widgets lightweight, it's recommended that you identify your widget's primary category, develop it according to that category, and then add a few elements from the widget's secondary category.

Does my project really need an application widget?

There's several reasons why you should consider adding an application widget to your Android project.

Widgets for Android can improve the user experience

As a general rule, the fewer navigational steps required to complete a task, the better the user experience.

By providing an application widget, you can remove multiple navigational steps from your app's most commonly-used flows. In the best case scenario, your users will be able to get the information they need just by glancing at their homescreen, or perform the desired task simply by tapping a button in your control widget.

More powerful than application shortcuts

App widgets often respond to onClick events by launching the top level in the associated application, similar to an application shortcut. However, widgets can also provide direct access to specific Activities within an application, for example tapping a widget's New Message Received notification might launch the associated app with the new message already open.

Widgets for Android Homescreen

By embedding multiple links in your widget's layout, you can provide one-tap access to all of your app's most important Activities, removing even more navigational steps from your most commonly-used flows.

By embedding multiple links in your widget's layout, you can provide one-tap access to all of your app's most important Activities.

Note that widgets respond to onClick events only, which prevents users from accidentally interacting with your widget while they're swiping around the homescreen. The only exception is when the user is attempting to delete your widget by dragging it towards their homescreen's Remove action, as in this scenario your widget will respond to a vertical swipe gesture.

This interaction is managed by the Android system, so you don't need to worry about manually implementing vertical swipe support in your widget.

Create an Android widget to drive long-term engagement

Convincing people to download your app is only the first step to creating a successful Android application. Chances are, if you grab your own Android smartphone or tablet and swipe through the app drawer, then you'll discover multiple apps that you haven't used in days, weeks or potentially even months!

Read also: Getting started with the Facebook for Android SDK

Once your app is successfully installed on the user's device, you'll need to work hard to keep them engaged and enjoying your app. Giving your app a presence on the homescreen can be a powerful tool to help drive long-term engagement, simply because it's a constant reminder that your application exists!

A well-designed widget can also serve as an ongoing advert for your app. Every time the user glances at their homescreen, your widget has the opportunity to actively encourage them to re-engage with your app, by presenting them with all of your app's most interesting and useful content.

Creating a collection app widget

In this tutorial, we'll be building a collection widget that displays an array as a scrollable ListView.

In this tutorial, we'll be building a scrollable collection widget.

To help you track the app widget lifecycle, this widget will also trigger various toasts as it moves through the different lifecycle states. Towards the end of this tutorial, we'll be enhancing our widget with a custom preview image that'll be displayed in Android's Widget Picker, and a configuration Activity, which will allow users to customize the widget before placing it on their homescreen.

Create a new Android project with the settings of your choice, and let's get started!

Building your widget's layout

To start, let's define the widget's user interface (UI).

Application widgets are displayed in a process outside your application, so you can only use layouts and Views that are supported by RemoteViews.

When building your layout, you're restricted to the following:

  • AnalogClock
  • Button
  • Chronometer
  • FrameLayout
  • GridLayout
  • ImageButton
  • ImageView
  • LinearLayout
  • ProgressBar
  • RelativeLayout
  • TextView
  • ViewStub
  • AdapterViewFlipper
  • GridView
  • ListView
  • StackView
  • ViewFlipper

Note that subclasses of the above classes and Views are not supported.

Create a new layout resource file named list_widget.xml. Since we'll be displaying our data using a ListView, this layout mainly serves as a container for a <ListView> element:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:layout_margin="@dimen/widget_margin">       <LinearLayout         android:layout_width="match_parent"         android:layout_height="match_parent"         android:background="#bbDEDFDE"         android:orientation="vertical">           <ListView             android:id="@+id/widget_list"             android:layout_width="match_parent"             android:layout_height="match_parent">         </ListView>     </LinearLayout>    </FrameLayout>

Populating the collection widget

Next, we need to create a data provider for our ListView. Create a new Java class named DataProvider.java and add the following:

import android.content.Context;  import android.content.Intent;  import android.widget.RemoteViews;  import android.widget.RemoteViewsService;    import java.util.ArrayList;  import java.util.List;    import static android.R.id.text1;  import static android.R.layout.simple_list_item_1;    public class DataProvider implements RemoteViewsService.RemoteViewsFactory {       List<String> myListView = new ArrayList<>();     Context mContext = null;       public DataProvider(Context context, Intent intent) {         mContext = context;     }       @Override     public void onCreate() {         initData();     }       @Override     public void onDataSetChanged() {         initData();     }       @Override     public void onDestroy() {       }       @Override     public int getCount() {         return myListView.size();     }       @Override     public RemoteViews getViewAt(int position) {         RemoteViews view = new RemoteViews(mContext.getPackageName(),         simple_list_item_1);                 view.setTextViewText(text1, myListView.get(position));                 return view;     }       @Override     public RemoteViews getLoadingView() {         return null;     }       @Override     public int getViewTypeCount() {         return 1;     }       @Override     public long getItemId(int position) {        return position;     }       @Override     public boolean hasStableIds() {         return true;     }       private void initData() {         myListView.clear();         for (int i = 1; i <= 15; i++) {             myListView.add("ListView item " + i);         }       }    }

AppWidgetProvider: Configuring your widget

To create an Android widget, you need to create several files.

Our first widget-specific file is an AppWidgetProvider, which is a BroadcastReceiver where you'll define the various widget lifecycle methods, such as the method that's called when your widget is first created and the method that's called when that widget is eventually deleted.

Create a new Java class (File > New > Java Class) named CollectionWidget.

To start, all widget provider files must extend from the AppWidgetProvider class. We then need to load the list_widget.xml layout resource file into a RemoteViews object, and inform the AppWidgetManager about the updated RemoteViews object:

public class CollectionWidget extends AppWidgetProvider {       static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,                       int appWidgetId) {    //Instantiate the RemoteViews object//           RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.list_widget);             setRemoteAdapter(context, views);    //Request that the AppWidgetManager updates the application widget//           appWidgetManager.updateAppWidget(appWidgetId, views);       }

Create the adapter

Since we're displaying our data in a ListView, we need to define a setRemoteAdapter() method in our AppWidgetProvider. The setRemoteAdapter() is equivalent to calling AbsListView.setRemoteViewsAdapter() but is designed to be used in application widgets.

In this method, we need to define the id of the AdapterView (R.id.widget_list) and the intent of the service that'll eventually provide the data to our RemoteViewsAdapter – we'll be creating this WidgetService class shortly.

private static void setRemoteAdapter(Context context, @NonNull final RemoteViews views) {     views.setRemoteAdapter(R.id.widget_list,          new Intent(context, WidgetService.class));  }    }

Defining the widget lifecycle methods

In our AppWidgetProvider, we also need to define the following widget lifecycle methods:

Retrieving new content with onUpdate

The onUpdate() widget lifecycle method is responsible for updating your widget's Views with new information.

This method is called each time:

  • The user performs an action that manually triggers the onUpdate() method.
  • The application's specified update interval has elapsed.
  • The user places a new instance of this widget on their homescreen.
  • An ACTION_APPWIDGET_RESTORED broadcast intent is sent to the AppWidgetProvider. This broadcast intent is triggered if the widget is ever restored from backup.

This is also where you'll register any event handlers that your widget should use.

When updating an Android widget, it's important to remember that users can create multiple instances of the same widget. For example, maybe your widget is customizable and the user decides to create several "versions" that display different information, or provide access to unique functionality.

When you call onUpdate(), you need to specify whether you're updating every instance of this widget, or a specific instance only. If you want to update every instance, then you can use appWidgetIds, which is an array of IDs that identifies every instance across the device.

In the following snippet, I'm updating every instance:

@Override  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {     for (int appWidgetId : appWidgetIds) {    //Update all instances of this widget//           updateAppWidget(context, appWidgetManager, appWidgetId);     }     super.onUpdate(context, appWidgetManager, appWidgetIds);  }

Note that to help keep the code straightforward, this onUpdate() method isn't currently making any changes to the widget.

onEnabled: Performing the initial setup

The onEnabled() lifecycle method is called in response to ACTION_APPWIDGET_ENABLED, which is sent when an instance of your widget is added to the homescreen for the first time. If the user creates two instances of your widget, then onEnabled() will be called for the first instance, but not for the second.

The onEnabled() lifecycle method is where you should perform any setup that's required for all instances of your widget, such as creating the database that'll feed your widget information.

I'm going to display a toast, so you can see exactly when this lifecycle method is called:

@Override  public void onEnabled(Context context) {     Toast.makeText(context,"onEnabled called", Toast.LENGTH_LONG).show();  }

Note that if the user deletes all instances of your widget and then creates a new instance, then this is classed as the first instance, and the onEnabled() lifecycle method will be called once again.

Cleaning up, with onDisabled

The onDisabled() method is called in response to ACTION_APPWIDGET_DISABLED, which is triggered when the user deletes the last instance of your widget.

This widget lifecycle method is where you should cleanup any resources you created in the onEnabled() method, for example deleting the database you created in onEnabled().

To help keep our code straightforward, I'll simply be displaying a toast every time this method is triggered:

@Override  public void onDisabled(Context context) {     Toast.makeText(context,"onDisabled called", Toast.LENGTH_LONG).show();  }

The completed AppWidgetProvider

Your CollectionWidget file should now look something like this:

import android.appwidget.AppWidgetManager;  import android.appwidget.AppWidgetProvider;  import android.content.Context;  import androidx.annotation.NonNull;  import android.content.Intent;  import android.widget.RemoteViews;  import android.widget.Toast;    //Extend from the AppWidgetProvider class//    public class CollectionWidget extends AppWidgetProvider {       static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,                       int appWidgetId) {    //Load the layout resource file into a RemoteViews object//           RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.list_widget);             setRemoteAdapter(context, views);    //Inform AppWidgetManager about the RemoteViews object//           appWidgetManager.updateAppWidget(appWidgetId, views);    }       @Override     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {         for (int appWidgetId : appWidgetIds) {             updateAppWidget(context, appWidgetManager, appWidgetId);         }         super.onUpdate(context, appWidgetManager, appWidgetIds);     }       @Override     public void onEnabled(Context context) {         Toast.makeText(context,"onEnabled called", Toast.LENGTH_LONG).show();     }         @Override     public void onDisabled(Context context) {         Toast.makeText(context,"onDisabled called", Toast.LENGTH_LONG).show();     }       private static void setRemoteAdapter(Context context, @NonNull final RemoteViews views) {         views.setRemoteAdapter(R.id.widget_list,                 new Intent(context, WidgetService.class));     }       }

The AppWidgetProviderInfo file

Your application widget also requires an AppWidgetProviderInfo file, which defines several important properties, including your widget's minimum dimensions and how frequently it should be updated.

The AppWidgetProviderInfo file is stored in your project's res/xml folder.

If your project doesn't already contain an XML file, then you'll need to create one.

If your project doesn't already contain this folder, then you'll need to create it:

  • Control-click your project's res folder.
  • Select New > Android Resource Directory.
  • In the subsequent window, open the Resource type dropdown, and select xml.
  • The Directory name should update to xml automatically, but if it doesn't then you'll need  to change it manually.
  • Click OK.

Next, create a collection_widget_info file, which we'll be using as our AppWidgetProviderInfo:

  • Control-click your project's xml folder.
  • Select New > XML resource file.
  • Name this file collection_widget_info.
  • Click OK.

In our AppWidgetProviderInfo file, we need to define the following properties:

1. android:previewImage

This is the drawable that represents your application widget in the device's Widget Picker.

Users can choose a widget from Android's Widget Picker.

If you don't provide a previewImage, then Android will use your application's icon instead. To encourage users to select your widget from the Widget Picker, you should provide a drawable that shows how your widget will look once it's properly configured on the user's homescreen.

The easiest way to create a preview image, is to use the Widget Preview application that's included in the Android emulator. This app lets you configure your widget and then generate an image, which you can then use in your Android project.

We'll be creating this image once we've finished building our widget, so for now I'll be using the automatically-generated mipmap/ic_launcher resource as a temporary preview image.

2. android:widgetCategory

Application widgets must be placed inside an App Widget Host, which is usually the stock Android homescreen, but can also be a third party launcher such as Evie Launcher or Nova Launcher.

Between API levels 17 and 20, it was possible to place application widgets on the homescreen or the lockscreen, but lockscreen support was deprecated in API level 21.

You can specify whether your app widget can be placed on the homescreen, the lockscreen (which Android refers to as the "keyguard") or both, using the android:widgetCategory attribute. Since it's not possible to place widgets on the lockscreen in the most recent versions of Android, we'll be targeting the homescreen only.

To preserve the user's privacy, your widget shouldn't display any sensitive or private information when it's placed on the lockscreen.

If you give users the option to place your widget on the lockscreen, then anyone who glances at the user's device could potentially see your widget, and all of its content. To help preserve the user's privacy, your widget shouldn't display any sensitive or private information when it's placed on the lockscreen. If your widget does contain personal data, then you may want to consider providing separate homescreen and lockscreen layouts.

3. android:initialLayout

This is the layout resource file that your widget should use when it's placed on the homescreen, which for our project is list_widget.xml.

4. android:resizeMode="horizontal|vertical"

The android:resizeMode attribute lets you specify whether your widget can be resized horizontally, vertically, or along both axes.

To ensure your widget displays and functions correctly across a variety of screens, it's recommended that you allow your widget to be resized horizontally and vertically, unless you have a specific reason not to.

5. android:minHeight and android:minWidth

If your widget is resizable, then you need to ensure the user doesn't shrink your widget to the point where it becomes unusable. You can use the minHeight and minWidth attributes to define the smallest your app will shrink when it's being resized by the user.

These values also represent your widget's initial size, so if your widget isn't resizable then minHeight and minWidth will define the widget's permanent sizing.

6. android:updatePeriodMillis

The AppWidgetProviderInfo is also where you'll specify how often your widget should request new information.

The smallest supported update interval is once every 1800000 milliseconds (30 minutes). Even if you declare a shorter update interval, your widget will still only update once every half an hour.

While you may want to display the latest information as quickly as possible, the system will wake a sleeping device in order to retrieve new information. Frequent updates can burn through a device's battery, particularly during periods where the device is left idle for a significant period of time, such as overnight. Providing the best possible user experience means striking a balance between limiting battery consumption, and providing new information within a reasonable time frame.

You should also take into account the kind of content your widget will display.

You should also take into account the kind of content your widgets for Android will display. For example, a weather widget may only need to retrieve an updated forecast once per day, whereas an app that displays breaking news will need to update more frequently.

To find this perfect balance, you may need to test your widget across a range of update frequencies and measure the impact on battery life, and the timeliness of your widget's content. If you have a willing group of testers, then you could even setup A/B testing, to see whether some update frequencies are received more positively than others.

Aalso read: AndroidManifest.xml everything you need to know

Finally, once you've identified the perfect update interval, you may want to use a shorter interval when developing and testing your app. For example, you could use the shortest possible update frequency (android:updatePeriodMillis="1800000″) when you're testing that your app's onUpdate() method is triggering correctly, and then change this value before releasing your app to the general public.

The completed AppWidgetProviderInfo

The finished collection_widget_info.xml file should look something like this:

<?xml version="1.0" encoding="utf-8"?>  <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"     android:previewImage="@mipmap/ic_launcher"     android:widgetCategory="home_screen"     android:initialKeyguardLayout="@layout/list_widget"     android:initialLayout="@layout/list_widget"     android:minHeight="100dp"     android:minWidth="100dp"     android:updatePeriodMillis="1800000"     android:resizeMode="horizontal|vertical">    </appwidget-provider>

Don't clutter the user's homescreen!

To ensure the homescreen never looks cluttered, we're going to add some padding and margins to our widget. If your project doesn't already contain a dimens.xml file, then you'll need to create one:

  • Control-click your project's values folder.
  • Select New > Values resource file.
  • Give this file the name dimens.
  • Click OK.

Open your dimens.xml file and define the following margin and padding values:

<resources>     <dimen name="widget_margin">10dp</dimen>     <dimen name="widget_header_padding">8dp</dimen>  </resources>

Sending data to the widget

Next, we need to create a widget service, which will be responsible for sending our collection data to the widget.

Create a new Java class (New > Java Class) named WidgetService, and add the following:

import android.content.Intent;  import android.widget.RemoteViewsService;    public class WidgetService extends RemoteViewsService {     @Override     public RemoteViewsFactory onGetViewFactory(Intent intent) {         return new DataProvider(this, intent);     }  }

Registering your widget in the Manifest

We now need to make some changes to our project's Manifest.

To start, open the Manifest and register your widget as a BroadcastReceiver. We also need to add an intent filter for the android.appwidget.action.APPWIDGET_UPDATE action:

     <receiver android:name=".CollectionWidget">             <intent-filter>                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />             </intent-filter>

Next, you need to specify the app widget provider:

           <meta-data                 android:name="android.appwidget.provider"                 android:resource="@xml/collection_widget_info" />         </receiver>

Finally, we need to declare the service that will send data to our widget, which in this instance is the WidgetService class. This service requires the android.permission.BIND_REMOTEVIEWS permission:

       <service android:name=".WidgetService"             android:permission="android.permission.BIND_REMOTEVIEWS" />

Put your widget to the test

If you've been following along with this tutorial, then you'll now have a complete collection widget that displays a set of data on the user's homescreen.

Create Widgets for Android

If this was a real-life Android project, then you'd typically expand on the lifecycle methods, particularly the onUpdate() method, but this is all we need to create a widget that you can install and test on your Android device:

  • Install this project on a compatible Android smartphone, tablet or AVD (Android Virtual Device).
  • Long-press any empty section of the homescreen, and select Widgets when prompted; this launches the Widget Picker.
  • Swipe through the Widget Picker until you find the application widget you just created.
  • Long-press this widget to add it to your homescreen.
  • Since this is the first instance of this particular widget, the onEnabled() method should run, and you'll see an "onEnabled called" toast.
  • Resize your widget. If you set a minimum supported size, then check that you cannot shrink the widget past this value.
  • Test that the ListView scrolls, as expected.
  • Next, you should check the onDisabled() method, by deleting your widget. Long-press the widget, and then select Remove from Home screen. Since this is the last instance of this particular widget, the onDisabled() method should run, and you'll see an "onDisabled called" toast.

This is all you need to deliver a functioning Android application widget, but there's a few additions that can often improve the user experience. In the following sections, we'll encourage users to choose this widget from the Widget Picker, by creating a preview image that showcases the widget at its best. I'll also show you how to create a fully-customizable widget, by adding a configuration Activity to your project.

Creating an Android widget preview image

If you grab your Android device and swipe through the Widget Picker, you'll see that every widget is represented by an image, which usually demonstrates how this widget will look once it's configured on the user's homescreen.

To encourage users to select your widget, you should provide a preview image that highlights all of the useful information and features that your widget has to offer.

You can quickly and easily create a preview image, using the Widget Preview application that's included in the Android emulator.

Note that Widget Preview isn't included in the latest Android system images, so you'll need to create an AVD using Nougat (API Level 25) or earlier:

  • Install your application on an AVD that's running API 25 or lower.
  • Open the AVD's app drawer and launch the Widget Preview app.
  • The Widget Preview will display a list of every application that's currently installed on this AVD; select your application from the list.

Create a preview image, using the Widget Preview application.

  • Your widget will now be displayed on a blank background. Spend some time resizing and tweaking your widget until it's showing the very best that your widget has to offer.
  • Once you're happy with your widget's appearance and content, select Take Snapshot.

Configure your widget and take a screenshot, by clicking the Camera button.

  • To retrieve your snapshot, switch back to Android Studio and select View > Tool Windows > Device File Explorer from the toolbar. This launches Android Studio's Device File Explorer.
  • In the Device File Explorer, navigate to sdcard/Download. You should find your preview image saved in the following format: [application_name]_ori_[orientation].png

Extract your screenshot from the AVD's "sdcard/download" folder.

  • Drag this image out of Android Studio and drop it somewhere easily accessible, such as your Desktop.
  • Give this image file a descriptive name.
  • Drag and drop the file into your project's drawable folder.
  • Open your AppWidgetProviderInfo, which for this project is collection_widget_info.xml.
  • Find the android:previewImage="@mipmap/ic_launcher" line and update it to reference your preview image.

Your widget will now use this new image resource as its preview image:

  • Install the updated project on your physical Android device or AVD.
  • Long-press any empty section of the homescreen.
  • Tap Widgets, which launches the Widget Picker.
  • Scroll to your widget; it should now be using the updated preview image.

Customizable widgets: Adding a configuration Activity

A configuration Activity launches automatically when the user places each instance of your widget on their homescreen.

There's several reasons why you might want to add a configuration Activity to your project.

widgets tend to provide the best user experience when they provide access to the information or features that are most important to the individual user.

Firstly, some widgets require initial setup, for example a widget that displays traffic alerts might need to know the user's home address, where they work, and the times when they typically commute. Without some way to enter this information, your widget might be completely useless!

In addition, widgets tend to provide the best user experience when they provide access to the information or features that are most important to the individual user. By adding a configuration Activity to your project, you can give users the freedom to pick and choose exactly what's included in your widget.

Even relatively straightforward customizations, such as changing a widget's background or font, can have a positive impact on the user experience – after all, no-one's going to appreciate a widget that visually clashes with the rest of their homescreen!

No-one's going to appreciate a widget that visually clashes with the rest of their homescreen!

Alternatively, sometimes you may have a long list of content that you want to include in your widget, and you're struggling to narrow down your options. A configuration Activity can be a way to put all of your ideas to good use, without creating a cluttered, confusing widget. Just bear in mind that setting up a widget shouldn't feel like a chore, so if you do provide a configuration Activity then it's recommended that you limit yourself to three configuration options.

Let's add a configuration Activity to our project!

Firstly, our configuration Activity needs a layout, so create a new layout resource file named config_activity.xml.

I'm going to add the following buttons to this layout:

  • A configuration button. In a real-life project, this button would modify the widget in some way, for example adding or removing content, or changing how frequently the widget updates. To help keep our code straightforward, clicking this button will simply display a Configuration Options toast.
  • A setup button. Once the user is happy with how their widget is configured, pressing this button will place the newly-configured widget on their homescreen.

Here's my completed config_activity.xml file:

<?xml version="1.0" encoding="utf-8"?>  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:orientation="vertical" android:layout_width="match_parent"      android:layout_height="match_parent">        <Button          android:id="@+id/configButton"          android:layout_width="fill_parent"          android:layout_height="wrap_content"          android:layout_alignParentLeft="true"          android:layout_marginTop="180dp"          android:text="Perform some configuration"          android:textColor="#808080" />        <Button          android:id="@+id/setupWidget"          android:layout_width="fill_parent"          android:layout_height="wrap_content"          android:layout_alignParentLeft="true"          android:layout_marginTop="180dp"          android:text="Create the widget"          android:textColor="#808080" />    </LinearLayout>

Create the Configuration Activity

Now, we need to create our configuration Activity.

To start, create a new Java class named ConfigActivity. In this Activity, we're going to retrieve the App Widget ID from the intent that launched the configuration Activity. If this intent doesn't have a widget ID, then we'll need to call the finish() method:

        Intent intent = getIntent();          Bundle extras = intent.getExtras();          if (extras != null) {              appWidgetId = extras.getInt(                      AppWidgetManager.EXTRA_APPWIDGET_ID,                      AppWidgetManager.INVALID_APPWIDGET_ID);         if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {                  finish();              }

Next, we need to create a return intent, pass the original appWidgetId and set the results from the configuration Activity:

            Intent resultValue = new Intent();              resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);              setResult(RESULT_OK, resultValue);              finish();          }      }  }

If you provide a configuration Activity, then the ACTION_APPWIDGET_UPDATE broadcast won't be sent automatically when the configuration Activity is launched, which means the onUpdate() method won't be called when the user creates an instance of your widget.

To ensure your widget is created with up-to-date information and content, your configuration Activity must trigger the first onUpdate() request.

Here's the completed ConfigActivity:

import android.app.Activity;  import android.appwidget.AppWidgetManager;  import android.os.Bundle;  import android.widget.Button;  import android.content.Intent;  import android.view.View;  import android.view.View.OnClickListener;  import android.widget.Toast;    public class ConfigActivity extends Activity {         @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.config_activity);           setResult(RESULT_CANCELED);           Button setupWidget = (Button) findViewById(R.id.setupWidget);           setupWidget.setOnClickListener(new OnClickListener() {                 @Override               public void onClick(View v) {                   handleSetupWidget();               }          });          Button configButton = (Button) findViewById(R.id.configButton);          configButton.setOnClickListener(new OnClickListener() {                @Override              public void onClick(View v) {                  handleConfigWidget();              }          });        }        private void handleSetupWidget() {          showAppWidget();        }        private void handleConfigWidget() {          Toast.makeText(ConfigActivity.this,                  "Configuration options", Toast.LENGTH_LONG).show();        }        int appWidgetId;      private void showAppWidget() {          appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;            Intent intent = getIntent();          Bundle extras = intent.getExtras();          if (extras != null) {              appWidgetId = extras.getInt(                     AppWidgetManager.EXTRA_APPWIDGET_ID,                     AppWidgetManager.INVALID_APPWIDGET_ID);                if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {                  finish();              }    //TO DO: Perform the configuration//                Intent resultValue = new Intent();              resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);              setResult(RESULT_OK, resultValue);              finish();          }      }  }

Once you've created a configuration Activity, you need to declare this Activity in the Manifest and specify that it accepts the APPWIDGET_CONFIGURE action:

       <activity android:name=".ConfigActivity">             <intent-filter>                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>             </intent-filter>          </activity>

Finally, since a configuration Activity is referenced outside of the package scope, we need to declare this Activity in our AppWidgetProviderInfo, which in this instance is the collection_widget_info.xml file:

   android:configure="com.jessicathornsby.collectionwidget.ConfigActivity">

Testing your project

Now it's time to put your finished project to the test:

  • Install your updated project on a physical Android device or AVD.
  • Delete all previous instances of your widget, to ensure you're working with the very latest version.
  • Long-press any empty area of the homescreen and select Widgets when prompted.
  • Find your widget in the Widget Picker and long-press to select it.
  • Drop the widget onto your homescreen. The Configuration Activity should launch automatically.
  • Give the Perform Some Configuration button a click, and a Configuration Options toast should appear, confirming that this interaction has been registered successfully.
  • Imagine that you've tweaked the widget's settings and are now ready to place it on your homescreen; give the Create The Widget button a tap, and this widget should be created successfully.

You can download the completed collection widget project from GitHub.

Wrapping up

How to Make an Android Widget

In this article, we created a scrollable collection widget that displays a data set on the user's homescreen.

If you want to continue working with this project, then you could try adding your own code to the onUpdate() method, to create a widget that updates with new information at the interval defined in your AppWidgetProviderInfo file (collection_widget_info).

If you do create an Android widget, then be sure to share your creations in the comments below!

More posts about Android Development

Save 60% on our Introduction to Android Development Course

Android Authority has created a course to help you learn how to develop Android Apps! No Coding Experience Required. Start Your Journey on Becoming an Android Developer today.
Visit DGiT Academy today. Enter discount code: SIXTYOFF at checkout to receive 60% off.


from Android Authority https://ift.tt/2NyzHBt
via IFTTT

No comments:

Post a Comment