android的widget基础用法

准备工作

app\src\main\res\xml\下新建一个appwidget-provider文件,文件名:test_widget_info
内容如下:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/test_widget" //widget 布局文件
    android:minWidth="300dp"  //widget最小宽度
    android:minHeight="200dp" // widget最小高度
    android:resizeMode="horizontal|vertical" 
    android:updatePeriodMillis="86400000"  // 最短刷新时间间隔
    android:previewImage="@drawable/widget_preview" //widget选择列表里的预览图
    android:widgetCategory="home_screen"></appwidget-provider>  // 类型 主屏,还有种是基于锁屏的

androidmanifest文件里注册下widget,可以看出widget是继承自receiver的。
public class AppWidgetProvider extends BroadcastReceiver
我们的widget继承AppWidgetProvider
AndroidManifest配置文件里加上

        <receiver android:name=".widget.appwidget.TestWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> // 这个action类似于广播的action
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/test_widget_info" />  //指向我们刚配置的文件
        </receiver>

写一个widget继承AppWidgetProvider

public class TestWidget extends AppWidgetProvider {


    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        //更新widget会调用这个方法
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        //删除widget时调用这个方法
    }

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        //widget被第一次添加时会调用这个方法,这个方法应该只会有系统来调用
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        // 最后一个widget被删除时会调用这个方法
    }
}

简单看下AppWidgetProvider里onReceive方法

public void onReceive(Context context, Intent intent) {
        // Protect against rogue update broadcasts (not really a security issue,
        // just filter bad broacasts out so subclasses are less likely to crash).
        String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (appWidgetIds != null && appWidgetIds.length > 0) {
                    this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                }
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context, new int[] { appWidgetId });
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
                    && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
                int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                Bundle widgetExtras = extras.getBundle(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS);
                this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
                        appWidgetId, widgetExtras);
            }
        } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
            this.onEnabled(context);
        } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
            this.onDisabled(context);
        } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null) {
                int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
                int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
                if (oldIds != null && oldIds.length > 0) {
                    this.onRestored(context, oldIds, newIds);
                    this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
                }
            }
        }
    }

具体UI相关的。
widget的view还比较特殊,叫RemoteViews
跟我们常见的view集成viewgroup不同,它继承的是Parcelable, Filter
自然不能用findviewbyid之类的,代码里动态设置布局什么的都不太好搞了
但官方也给我们开放了一些接口用来设置控件。

基础的 设置textview,imageview


RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.test_widget); //test_widget.xml 就是widgetview的布局文件
views.setTextViewText(R.id.tv, "helloworld");
views.setImageViewResource(R.id.iv,R.drawable/test_iv);

设置点击事件


        Intent intent = new Intent(context, NewActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(context, UUID.randomUUID().hashCode(),
                upload, PendingIntent.FLAG_UPDATE_CURRENT);//widget如果有多个点击事件需要这么写,否则点击事件只响应最先设置的那个
        views.setOnClickPendingIntent(R.id.btn, pIntent);

结束语:
基础的东西大概就这么多了。
个人觉得remoteview需要仔细研究下源码,不然没法使用一些图片加载框架,很不方便

另外widget也不支持constraint-layout,(手头华为,三星,vivo,小米都试了不行),很不美好
强上后报的异常日志

 Caused by: java.lang.ClassNotFoundException: Didn't find class "android.support.constraint.ConstraintLayout" on path: DexPathList[[zip file "/system/app/HwLauncher6/HwLauncher6.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
        at android.view.LayoutInflater.createView(LayoutInflater.java:590)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:762)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:501) 
        at android.view.LayoutInflater.inflate(LayoutInflater.java:425) 
        at android.widget.RemoteViews.apply(RemoteViews.java:2636) 
        at android.appwidget.AppWidgetHostView.updateAppWidget(AppWidgetHostView.java:401) 
        at android.appwidget.AppWidgetHost.createView(AppWidgetHost.java:325) 
        at com.huawei.android.launcher.Launcher.addWidgetToScreen(Launcher.java:3053) 
        at com.huawei.android.launcher.Launcher.completeAddAppWidget(Launcher.java:3118) 
        at com.huawei.android.launcher.Launcher.addAppWidgetImpl(Launcher.java:4056) 
        at com.huawei.android.launcher.Launcher.addAppWidgetFromDrop(Launcher.java:4130) 
        at com.huawei.android.launcher.Workspace.addPendingAddItemInfoToDesktop(Workspace.java:9914) 
        at com.huawei.android.launcher.Workspace$9.run(Workspace.java:5676) 
        at com.huawei.android.launcher.DragLayer$3.onAnimationEnd(DragLayer.java:982) 
        at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1171) 
        at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:722) 
        at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:738) 
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:799) 
        at android.view.Choreographer.doCallbacks(Choreographer.java:612) 
        at android.view.Choreographer.doFrame(Choreographer.java:580) 
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:785) 
        at android.os.Handler.handleCallback(Handler.java:739) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:135) 
        at android.app.ActivityThread.main(ActivityThread.java:5593) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:372) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:967) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:762) 
        Suppressed: java.lang.ClassNotFoundException: android.support.constraint.ConstraintLayout
        at java.lang.Class.classForName(Native Method)
        at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
        at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
                ... 30 more

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345