Android weidget小组件 列表widget

转载请注明原创出处,谢谢!
效果图.png

AndroidManifest.xml

        <receiver
            android:name=".widget_card.LivePlanWidget"
            android:exported="true"
            android:label="直播计划">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="com.test.liveplanwidget.refresh" />
                <action android:name="com.test.liveplanwidget.click.item" />
            </intent-filter>

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

        <service
            android:name=".widget_card.LivePlanRemoteViewsService"
            android:exported="true"
            android:permission="android.permission.BIND_REMOTEVIEWS" />
  • 注意android.permission.BIND_REMOTEVIEWS是必须要有的

res/xml/widget_provider_info_live_plan.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/app_widget_description_live_plan"
    android:initialKeyguardLayout="@layout/widget_live_plan"
    android:initialLayout="@layout/widget_live_plan"
    android:minWidth="200dp"
    android:minHeight="100dp"
    android:previewImage="@drawable/icon_widget_live_plan_preview"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="1800000"
    android:widgetCategory="home_screen" />
public class LivePlanWidget extends AppWidgetProvider {

    private static final String TAG = LivePlanWidget.class.toString() + "aaaaaaaa";

    public static final String ACTION_REFRESH = "com.test.liveplanwidget.refresh";
    public static final String ACTION_CLICK_ITEM = "com.test.liveplanwidget.click.item";

    private CompositeDisposable mCompositeDisposable = null;

    /**
     * 调用此方法可以按 AppWidgetProviderInfo 中的 updatePeriodMillis 属性定义的时间间隔来更新微件。
     */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.i(TAG, "onUpdate: " + Arrays.toString(appWidgetIds));
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
            AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_live_plan);
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (ACTION_CLICK_ITEM.equals(intent.getAction())) {
            // list item 点击事件
            Intent startIntent = new Intent(context, MainActivity.class);
            startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startIntent.putExtra("from_widget", "live_plan_widget");
            startIntent.putExtra("to_activity", "main");
            startIntent.putExtra("widget_key", intent.getStringExtra("widget_key"));
            context.startActivity(startIntent);
        } else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(intent.getAction())) {
            Log.i(TAG, "onReceive: 收到更新广播");
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
            Log.i(TAG, "onReceive: getAction " + appWidgetId + "    " + intent.getAction());
            okhttp(context, AppWidgetManager.getInstance(context), appWidgetId);
        } else {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
            Log.i(TAG, "onReceive: getAction " + appWidgetId + "    " + intent.getAction());
            if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
                updateAppWidget(context, AppWidgetManager.getInstance(context), appWidgetId);
                AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_live_plan);
            } else {
                // 收到0
                Log.i(TAG, "onReceive: 收到appwidgetId为0的广播");
                int[] appWidgetIds = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, LivePlanWidget.class));
                Log.i(TAG, "onReceive 收到appwidgetId为0的广播->: " + Arrays.toString(appWidgetIds));
                for (int id : appWidgetIds) {
                    updateAppWidget(context, AppWidgetManager.getInstance(context), id);
                    AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, R.id.list_live_plan);
                }
            }
        }
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear();
        }
        mCompositeDisposable = null;
    }

    /**
     * 更新显示
     */
    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        try {
            // 构造RemoteViews对象
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_live_plan);
            int localUserId = PreferencesUtils.getInt(context, Constant.USERIDKEY);
            if (localUserId == Constant.USERDEFAULTID) {
                // 未登录
                views.setViewVisibility(R.id.tv_title, View.GONE);
                views.setViewVisibility(R.id.tv_count, View.GONE);
                views.setViewVisibility(R.id.iv_arr, View.GONE);
                views.setViewVisibility(R.id.list_live_plan, View.GONE);
                views.setViewVisibility(R.id.ll_empty, View.GONE);
                views.setViewVisibility(R.id.tv_login, View.VISIBLE);
            } else {
                // 登录
                String data = PreferencesUtils.getString(context, SettingConstant.SP_KEY_LIVE_PLAN_WIDGET_DATA);
                List<HomeLiveBean.EntityBean> list = null;
                if (!TextUtils.isEmpty(data)) {
                    list = new Gson().fromJson(data, new TypeToken<List<HomeLiveBean.EntityBean>>() {
                    }.getType());
                }
                if (list == null || list.size() == 0) {
                    // 无数据
                    views.setViewVisibility(R.id.tv_title, View.VISIBLE);
                    views.setViewVisibility(R.id.tv_count, View.GONE);
                    views.setViewVisibility(R.id.iv_arr, View.GONE);
                    views.setViewVisibility(R.id.list_live_plan, View.GONE);
                    views.setViewVisibility(R.id.ll_empty, View.VISIBLE);
                    views.setViewVisibility(R.id.tv_login, View.GONE);
                } else {
                    // 有数据
                    views.setViewVisibility(R.id.tv_title, View.VISIBLE);
                    views.setViewVisibility(R.id.tv_count, View.VISIBLE);
                    views.setViewVisibility(R.id.iv_arr, View.VISIBLE);
                    views.setViewVisibility(R.id.list_live_plan, View.VISIBLE);
                    views.setViewVisibility(R.id.ll_empty, View.GONE);
                    views.setViewVisibility(R.id.tv_login, View.GONE);

                    int curDayCount = 0;
                    String curStr = TimeUtils.date2Str(new Date(), "yyyy-MM-dd");
                    for (HomeLiveBean.EntityBean item : list) {
                        String lessonStartTime = item.getLessonStartTime();
                        if (curStr.equals(lessonStartTime.substring(0, 10))) {
                            curDayCount++;
                        }
                    }
                    if (curDayCount > 0) {
                        views.setTextViewText(R.id.tv_count, "今日共" + curDayCount + "场");
                    } else {
                        views.setTextViewText(R.id.tv_count, "全部共" + list.size() + "场");
                    }

                    // 设置列表数据
                    Intent intent = new Intent(context, LivePlanRemoteViewsService.class);
                    views.setRemoteAdapter(R.id.list_live_plan, intent);

                    // 设置点击事件
                    Intent gridIntent = new Intent(context, LivePlanWidget.class);
                    gridIntent.setAction(ACTION_CLICK_ITEM);
                    gridIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, gridIntent, PendingIntent.FLAG_UPDATE_CURRENT);
                    // 设置intent模板
                    views.setPendingIntentTemplate(R.id.list_live_plan, pendingIntent);
                }
            }

            // 点击进入首页
            Intent intent = new Intent(context, MainActivity.class);
            intent.putExtra("from_widget", "live_plan_widget");
            intent.putExtra("to_activity", "main");
            PendingIntent pendingIntent;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
            } else {
                pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_ONE_SHOT);
            }
            views.setOnClickPendingIntent(R.id.ll_root, pendingIntent);

            // 更新小部件
            appWidgetManager.updateAppWidget(appWidgetId, views);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 网络请求数据
     */
    private void okhttp(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        try {
            if (mCompositeDisposable == null) {
                mCompositeDisposable = new CompositeDisposable();
            }
            String userId = String.valueOf(PreferencesUtils.getInt(context, Constant.USERIDKEY));
            if (TextUtils.isEmpty(userId) || "0".equals(userId)) {
                updateAppWidget(context, appWidgetManager, appWidgetId);
                return;
            }
            TreeMap<String, String> paramsMap = ParameterUtils.getParamsMap();
            mCompositeDisposable.add(
                    new HomeModel().getMyLiveList(Address.AUTHORIZATION_CODE, ParameterUtils.getSign(paramsMap), paramsMap.get(Constant.TIME_STAMP), userId)
                            .subscribe(homeLiveBean -> {
                                try {
                                    if (homeLiveBean.isSuccess()) {
                                        PreferencesUtils.putString(context, SettingConstant.SP_KEY_LIVE_PLAN_WIDGET_DATA, new Gson().toJson(homeLiveBean.getEntity()));
                                    } else {
                                        PreferencesUtils.putString(context, SettingConstant.SP_KEY_LIVE_PLAN_WIDGET_DATA, "");
                                    }
                                    // 更新小组件
                                    updateAppWidget(context, appWidgetManager, appWidgetId);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }, throwable -> throwable.printStackTrace()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 使用AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, R.id.list_live_plan);方法来更新列表数据
public class LivePlanRemoteViewsService extends RemoteViewsService {
    private static final String TAG = LivePlanRemoteViewsService.class.toString() + "aaaaaaaa";

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        Log.i(TAG, "onGetViewFactory: ");
        return new LivePlanRemoteViewsFactory(this, intent);
    }
}
public class LivePlanRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static final String TAG = LivePlanRemoteViewsFactory.class.toString() + "aaaaaaaaaaaaaaa";
    private Context mContext;
    private int mAppWidgetId;

    private List<HomeLiveBean.EntityBean> list = new ArrayList<>();

    // 加一层bitmap缓存,解决glide加载错乱的问题
    private LruCacheUtils cache = new LruCacheUtils();

    public LivePlanRemoteViewsFactory(Context context, Intent intent) {
        this.mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        Log.i(TAG, "LivePlanRemoteViewsFactory: " + mAppWidgetId);
    }

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate: ");
        String data = PreferencesUtils.getString(mContext, SettingConstant.SP_KEY_LIVE_PLAN_WIDGET_DATA);
        if (!TextUtils.isEmpty(data)) {
            list = new Gson().fromJson(data, new TypeToken<List<HomeLiveBean.EntityBean>>() {
            }.getType());
        }
    }

    @Override
    public void onDataSetChanged() {
        Log.i(TAG, "onDataSetChanged: ");
        String data = PreferencesUtils.getString(mContext, SettingConstant.SP_KEY_LIVE_PLAN_WIDGET_DATA);
        if (!TextUtils.isEmpty(data)) {
            list = new Gson().fromJson(data, new TypeToken<List<HomeLiveBean.EntityBean>>() {
            }.getType());
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy: ");
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public RemoteViews getViewAt(int position) {
        Log.i(TAG, "getViewAt: " + position);
        try {
            HomeLiveBean.EntityBean item = list.get(position);
            if (item != null) {
                RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item_live_plan);

                Glide.with(mContext).asBitmap()
                        .load(R.drawable.bg_zhibojihua_app)
                        .transform(new CenterCrop(), new RoundedCorners(GeneralUtil.dip2px(mContext, 6)))
                        .into(new CustomTarget<Bitmap>() {
                            @Override
                            public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                                views.setImageViewBitmap(R.id.iv_img_bg, resource);
                            }

                            @Override
                            public void onLoadCleared(@Nullable Drawable placeholder) {
                            }
                        });

                if (cache == null) {
                    cache = new LruCacheUtils();
                }
                Bitmap bitmap = cache.get(item.getTeacher().getImageMap().getMobileUrlMap().getLarge());
                if (bitmap != null) {
                    views.setImageViewBitmap(R.id.iv_img, bitmap);
                } else {
                    Glide.with(mContext).asBitmap()
                            .diskCacheStrategy(DiskCacheStrategy.ALL)
                            .apply(new RequestOptions().override(100, 100))
                            .load(item.getTeacher().getImageMap().getMobileUrlMap().getLarge())
                            .into(new CustomTarget<Bitmap>() {
                                @Override
                                public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                                    Log.i(TAG, "getViewAt:获取图片成功  " + position + "  " + item.getTeacher().getImageMap().getMobileUrlMap().getLarge());
                                    cache.put(item.getTeacher().getImageMap().getMobileUrlMap().getLarge(), resource);
                                    views.setImageViewBitmap(R.id.iv_img, resource);
                                }

                                @Override
                                public void onLoadCleared(@Nullable Drawable placeholder) {
                                }
                            });
                }

                views.setTextViewText(R.id.tv_title, item.getCatalogName());
                views.setTextViewText(R.id.tv_time,
                        TimeUtils.date2Str(TimeUtils.str2Date(item.getLessonStartTime(), "yyyy-MM-dd HH:mm:ss"), "MM月dd日 HH:mm")
                                + "-" + TimeUtils.date2Str(TimeUtils.str2Date(item.getLessonEndTime(), "yyyy-MM-dd HH:mm:ss"), "HH:mm")
                );
                views.setViewVisibility(R.id.tv_span, item.getCourseType() == 2 ? View.VISIBLE : View.GONE);
                views.setViewVisibility(R.id.tv_public_tag, item.getCourseType() == 2 ? View.VISIBLE : View.GONE);

                // 点击事件
                Intent fillInIntent = new Intent(mContext, LivePlanWidget.class);
                fillInIntent.putExtra("widget_key", String.valueOf(item.getId()));
                views.setOnClickFillInIntent(R.id.ll_root, fillInIntent);
                return views;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @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 false;
    }
}

  • 使用glide加载网络图片,会有明显的错乱和不渲染的情况,所以用了一层LruCache缓存

list_live_plan.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@android:id/background"
    android:layout_gravity="top"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/icon_widget_live_plan_bg"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/ll_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/dp_16">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="@dimen/dp_20"
                android:layout_height="@dimen/dp_20"
                android:src="@drawable/icon_widget_live_plan_logo" />

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/dp_8"
                android:layout_weight="1"
                android:text="直播计划"
                android:textColor="@color/gray_0"
                android:textSize="@dimen/sp_18"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tv_count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="今日共x场"
                android:textColor="@color/gray_70"
                android:textSize="@dimen/sp_12" />

            <ImageView
                android:id="@+id/iv_arr"
                android:layout_width="@dimen/dp_18"
                android:layout_height="@dimen/dp_18"
                android:background="@drawable/icon_widget_live_plan_arr" />
        </LinearLayout>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:minHeight="@dimen/dp_110">

            <ListView
                android:id="@+id/list_live_plan"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_6"
                android:divider="@null"
                android:listSelector="@color/transparent"
                tools:listitem="@layout/widget_item_live_plan" />

            <LinearLayout
                android:id="@+id/ll_empty"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:orientation="vertical">

                <ImageView
                    android:layout_width="@dimen/dp_90"
                    android:layout_height="@dimen/dp_90"
                    android:src="@drawable/icon_empty_service_course" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="暂无直播计划"
                    android:textColor="@color/gray_70"
                    android:textSize="@dimen/sp_11" />
            </LinearLayout>

            <TextView
                android:id="@+id/tv_login"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="登录查看直播计划"
                android:textColor="@color/gray_50"
                android:textSize="@dimen/sp_11"
                android:visibility="gone"
                tools:visibility="visible" />
        </FrameLayout>
    </LinearLayout>
</FrameLayout>

widget_item_live_plan.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_10"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <FrameLayout
            android:layout_width="@dimen/dp_58"
            android:layout_height="@dimen/dp_40">

            <ImageView
                android:id="@+id/iv_img_bg"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop" />

            <ImageView
                android:id="@+id/iv_img"
                android:layout_width="@dimen/dp_40"
                android:layout_height="@dimen/dp_40"
                android:layout_gravity="center"
                android:scaleType="centerCrop"
                android:src="@drawable/default_place_square_img" />
        </FrameLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_8"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:maxLines="1"
                android:text="论文高效写作法:写作技巧+案例模版"
                android:textColor="@color/gray_20"
                android:textSize="@dimen/sp_14" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_6"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/tv_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:maxLines="1"
                    android:text="0x月0x日 17:00-21:00"
                    android:textColor="@color/gray_50"
                    android:textSize="@dimen/sp_11" />

                <TextView
                    android:id="@+id/tv_span"
                    android:layout_width="@dimen/dp_1"
                    android:layout_height="@dimen/dp_6"
                    android:layout_marginStart="@dimen/dp_5"
                    android:layout_marginEnd="@dimen/dp_5"
                    android:background="@color/gray_90" />

                <TextView
                    android:id="@+id/tv_public_tag"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="公开课"
                    android:textColor="@color/green_n"
                    android:textSize="@dimen/sp_11" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>
public class LruCacheUtils {
    private LruCache<String, SoftReference<Bitmap>> cache = new LruCache<>(10);

    public Bitmap get(String id) {
        SoftReference<Bitmap> ref = cache.get(id);
        if (ref != null) {
            return ref.get();
        }
        return null;
    }

    public void put(String id, Bitmap bitmap) {
        cache.put(id, new SoftReference<>(bitmap));
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容