Android小部件Widget开发过程中的坑和问题小结

时间:2021-05-19

概述

官方参考

Build an App Widget

效果图

放张效果图,这是我玩的桌面 app 文件夹

AndroidManifest.xml

Receiver

切记里面的字母不要弄错,最好复制粘贴再修改相对应自定义的地方就好,一个字母的错误搞了我一天,吐血

<receiver android:name=".desktop.DesktopWidget"> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_desktop_options" /> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter></receiver>

Service

如果小部件中使用到了列表项如 ListView,GridView 等,在绑定数据时需要使用 RemoteViewsService 并提供一个 RemoteViewsFactory 实例来填充数据 而非 Adapter
再提,这里面一定不能敲错字母,特别是那个 permission 也一定要有,不然无法绑定数据

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

Options

res/xml/

widget_desktop_options.xml

<?xml version="1.0" encoding="utf-8"?><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" android:minHeight="40dp" android:initialLayout="@layout/widget_desktop" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen"></appwidget-provider>

常用参数

Size

  • 尺寸大小最终以单元格数据来显示,但定义时为 dp
  • 单元格数转换基本工式 size = 70 x cells - 30
  • 如:1格 = 70 x 1 - 30 = 40dp
  • 最小尺寸定义时最好不要超过 4 个单元格就是 250dp

updatePeriodMillis

更新时间毫秒数,即间隔多少时间呼叫一次 onUpdate() 方法

initialLayout

加载布局文件

Using App Widgets with Collections

小部件中使用列表项

官方参考

Google Develper

AppWidgetProvider

public class DesktopWidget extends AppWidgetProvider { public String ACTION_START_ACTIVITY = "STARTACTIVITYACTION"; public DesktopWidget() { super(); } //当接收到广播的时候会被调用 //onUpdate, onEnabled等时都会发送广播 @Override 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); pub.log(appIntent.getPackage()); if (appIntent != null) { appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); pub.log("start activity"); context.startActivity(appIntent); pub.log("finish"); } } } //被添加或者被更新时调用 @Override 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); //设定小部件上 TextView 值 //views.setTextViewText(R.id.tv_widget_desktop_title, "ZGTools"); //setRemoteAdapter设定Listivew,Gridview等复杂布局的adapter //第一个参数为要绑定的widget的Gridview的id,第二个参数为RemoteViewsService的intent Intent intent = new Intent(context, DesktopViewsService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); views.setRemoteAdapter(R.id.gv_app_group, intent); //绑定 item 的定点事件 //Activity方式测试不成功,改用广播的方式进行发送并执行事件 //此处不能是空的Intent 否则广播无法发送 //定义Intent事件模板,相当于公用设定,再在fillIntent里面设定具体的 extra 值 Intent templateIntent = new Intent(context, DesktopWidget.class); templateIntent.setAction(ACTION_START_ACTIVITY); templateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); templateIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); 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); } //被添加或者更改大小时调用,在这个方法中可以选择性的根据界面大小显示或隐藏某些控件 @Override public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); updateAppWidget(context, appWidgetManager, appWidgetId); } @Override public void onEnabled(Context context) { super.onEnabled(context); // Enter relevant functionality for when the first widget is created } @Override public void onDisabled(Context context) { super.onDisabled(context); // Enter relevant functionality for when the last widget is disabled }}

RemoteViewsService

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

RemoteViewsFactory

类型于 BaseAdapter,重点是 onCreate() 加载数据, getViewAt() 方法返回布局绑定数据

public class DesktopViewsFactory implements RemoteViewsService.RemoteViewsFactory { private Context mContext; private int mAppWidgetId; private List<AppPackage> lstApps = new ArrayList<AppPackage>(); public DesktopViewsFactory(Context context, Intent intent){ this.mContext = context; this.mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } @Override 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) { lstApps.clear(); 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) { cursor.close(); } } catch (Exception e) { db.close(); e.printStackTrace(); } db.close(); } @Override public void onDataSetChanged() { //这里我加了重新加载数据的调用,用于更新小部件的列表内容 onCreate(); } @Override public void onDestroy() {} @Override public int getCount() {return 0;} @Override 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()); intent.putExtras(bundle); views.setOnClickFillInIntent(R.id.iv_widget_desktop_icon, intent); views.setOnClickFillInIntent(R.id.tv_widget_desktop_label, intent); return views; } @Override public RemoteViews getLoadingView() { return null;} @Override public int getViewTypeCount() {return 0;} @Override public long getItemId(int i) { return 0;} @Override public boolean hasStableIds() { return false; }}

DesktopActivity

//更新widget布局 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); }

总结

到此这篇关于Android小部件Widget开发过程中的坑和总结的文章就介绍到这了,更多相关Android小部件Widget内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章