Pit and summary in the development of Android widget




Official reference

Build an App Widget

design sketch

Put a rendering. This is the desktop app folder I play
Pit and summary in the development of Android widget



Remember not to make mistakes in the letters inside. It’s best to copy and paste and then modify the corresponding custom places. One letter mistake made me vomit blood all day


If list items such as listview and GridView are used in the widget, you need to use remoteviewsservice and provide a remoteviewsfactory instance to fill in the data instead of the adapter when binding data
Again, there must be no wrong letters, especially the permission, otherwise the data cannot be bound




Common parameters

  • The size is finally displayed as cell data, but is defined as DP

  • Cell number conversion basic formula size = 70 x cells – 30

  • For example: 1 grid = 70 x 1 – 30 = 40dp

  • When defining the minimum size, it is best not to exceed 4 cells, that is, 250dp


Update time in milliseconds, that is, how often the onupdate() method is called


Load layout file

Using App Widgets with Collections

List items used in widgets

Official reference

Google Develper


public class DesktopWidget extends AppWidgetProvider {


    public DesktopWidget() {

    //Called when a broadcast is received
    //Broadcast will be sent when onupdate, onenabled, etc
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        pub.log("onReceive:" + intent.getAction());
        if (intent.getAction().equals(ACTION_START_ACTIVITY)) {
            String strPackage = intent.getStringExtra("app_package");
            Intent appIntent = context.getPackageManager().getLaunchIntentForPackage(strPackage);
            if (appIntent != null) {
                pub.log("start activity");

    //Called when added or updated
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        pub.log("onUpdate:" + appWidgetId);
        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_desktop);
        //Set textview value on widget
        //views.setTextViewText(R.id.tv_widget_desktop_title, "ZGTools");
        //Setremoteadapter sets adapters with complex layouts such as listivew and GridView
        //The first parameter is the ID of the GridView of the widget to be bound, and the second parameter is the intent of the remoteviews service
        Intent intent = new Intent(context, DesktopViewsService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        views.setRemoteAdapter(R.id.gv_app_group, intent);

        //Fixed point event of binding item
        //The activity mode test was not successful. Use broadcast mode to send and execute events
        //This cannot be an empty intent, otherwise the broadcast cannot be sent
        //Define the intent event template, which is equivalent to public settings, and then set the specific extra value in fillintent
        Intent templateIntent = new Intent(context, DesktopWidget.class);
        templateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, templateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setPendingIntentTemplate(R.id.gv_app_group, pendingIntent);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);

    //Called when the size is added or changed. In this method, some controls can be selectively displayed or hidden according to the interface size
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        updateAppWidget(context, appWidgetManager, appWidgetId);
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled


public class DesktopViewsService extends RemoteViewsService {
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new DesktopViewsFactory(this, intent);


The type is baseadapter. The key point is oncreate() to load data, and getviewat() method to return layout binding data

public class DesktopViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private Context mContext;
    private int mAppWidgetId;
    private List lstApps = new ArrayList();

    public DesktopViewsFactory(Context context, Intent intent){
        this.mContext = context;
        this.mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);

    public void onCreate() {
        DesktopDbHelper dbHelper = new DesktopDbHelper(mContext);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        String strSql = "Select * from tb_app";
        try {
            Cursor cursor = db.rawQuery(strSql, null);
            if (cursor != null && cursor.getCount() > 0) {
                while (cursor.moveToNext()) {
                    byte[] blob = cursor.getBlob(cursor.getColumnIndex("app_icon"));
                    Bitmap bmpIcon = BitmapFactory.decodeByteArray(blob, 0, blob.length);
                    String strLabel = cursor.getString(cursor.getColumnIndex("app_label"));
                    String strPackage = cursor.getString(cursor.getColumnIndex("app_package"));
                    lstApps.add(new AppPackage(strLabel, strPackage, bmpIcon));
            if (cursor != null) {
        } catch (Exception e) {

    public void onDataSetChanged() {
    	//Here I added a call to reload the data to update the list content of the widget
    public void onDestroy() {}
    public int getCount() {return 0;}

    public RemoteViews getViewAt(int i) {
        if (i < 0 || i >= lstApps.size()) return null;
        //Construct a remote views item based on the app widget item XML file
        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_desktop_item);
        views.setTextViewText(R.id.tv_widget_desktop_label, lstApps.get(i).getAppLabel());
        views.setImageViewBitmap(R.id.iv_widget_desktop_icon, lstApps.get(i).getAppIcon());

        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("app_package", lstApps.get(i).getAppPackage());
        views.setOnClickFillInIntent(R.id.iv_widget_desktop_icon, intent);
        views.setOnClickFillInIntent(R.id.tv_widget_desktop_label, intent);

        return views;

    public RemoteViews getLoadingView() { return null;}
    public int getViewTypeCount() {return 0;}
    public long getItemId(int i) { return 0;}
    public boolean hasStableIds() { return false; }


//Update widget layout
    private void updateWidget(){
        AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
        ComponentName componentName = new ComponentName(this, DesktopWidget.class);
        int[] ids = widgetManager.getAppWidgetIds(componentName);
        widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.gv_app_group);