zoukankan      html  css  js  c++  java
  • Android小部件Widget开发过程中的坑和总结

    @

    概述

    官方参考

    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);
        }
    
  • 相关阅读:
    MVC 中301永久重定向
    String
    redis key设置过期时间
    hbase java 增加列族
    hbase 物理存储
    java 类图
    SSH hql中文查询时乱码快速解决
    json和pickle的序列化与反序列化
    python之生成器与迭代器
    安装traits库心得
  • 原文地址:https://www.cnblogs.com/ymtianyu/p/13750302.html
Copyright © 2011-2022 走看看