萌新安卓笔记--桌面小程序的开发--AppWidget的应用

AppWidget应用场合

这东西常用于桌面小程序。比如华为手机自带的备忘录。。天气,一键锁屏啥的。可以学来玩玩。

应用AppWidget的前戏

    <receiver android:name=".MyWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

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

这个加到AndroidManifest中,其中name是类名,intent-filter是关键,没他系统就不会把他当成小程序。resource就是他的布局文件。

然后新建一个xml文件夹(其实value文件夹里新建xml也可以)

app_provider_activity_info.xml:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/app_provider_activity"
    android:minWidth="300dp"
    android:minHeight="200dp"
    android:updatePeriodMillis="86400000"></appwidget-provider>

这里可以用来规定appwidget的刷新时间,最小宽度以及布局文件的指定(就是initialLayout)。其实还有其他可以配置的,但最重要的就这几个了。

布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#456ffd"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="备忘录"/>
    <ListView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:id="@+id/list_1"/>
</LinearLayout>

(想复刻个备忘录来着。。)
ps:appwidget只支持有限的view种类和layout布局。流行的recyclerView和自定义view是不支持的。
绑定完布局后就是重点来了。。。

appwidget重头戏

首先这个东西继承自AppWidgetProvider,关于这个类其实和广播接收器差不多,做开发的时候直接视为广播接收器也未尝不可。。。先说下重写频率比较高的几个函数。

1. onReceive
这个是appWidget用来接收广播的地方,根据广播可以进行不同的操作
2.onUpdate
顾名思义,就是更新的时候调用,在初始化的时候就会调用一次,然后就是等之前配置文件设定的时间或者自己操作更新。
3.onEnable
第一次添加到桌面的时候才会调用。一般用来定义静态变量,以及配置服务。。。
4.onDelete
删除一次调用一次。。不过appwidget里不用像广播一样解除广播。

。。一般业务开展也就在onReceive和onUpdate吧。。。

ok,了解完这些后进入下一步。。点击事件。。不能点击的界面效果就会被大大局限。
appwidget里button,imageView这类单个的点击事件还是简单的。

//onupdate代码
Intent intentClick = new Intent();
intentClick.setAction("waibibabu");
PendingIntent pendingIntent = PendingIntent.getBroadCast(context,0,intentClick,0);
remoteViews.setOnClickPendingIntent(R.id.button1,pendingIntent);
appWidgeManager.updateAppWidget(appWidgetId,remoteViews);
//onReceive代码
switch(intent.getAction){
	case "waibibabu":
		Log.d("niuniu","hello,world");
		break;
}

这样一个简单的点击响应事件就做好了。
接下来就是重重重头,也是我写这篇博客的原因--------listView这类集合型view的点击响应事件。在Android中使用listview不是直接findViewByid然后绑定适配器,就好了嘛。但appwidget里不支持findViewById。。他靠的是两个关键函数setPendingIntentTemplate和setOnClickFillInIntent。

onUpdate里的setPendingIntentTemplate

   Intent intent = new Intent(context, MyWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            //设置RemoteViews的展示界面
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.app_provider_activity);
            //绑定适配器,这里会发送一个intent到MyWidgetService。
            views.setRemoteAdapter(R.id.list_1, intent);

首先设一个intent用来绑定provider和RemoteViewsService。。这个RemoteViewsService就是处理listView的item的关键。这里的setRemoteAdapter就是把remoteViews和RemoteViewsService绑定。

 Intent clickIntent = new Intent(context,MyWidget.class);
            clickIntent.setAction("clickAction");
            clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
            clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));

            PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(context,0,clickIntent,PendingIntent.FLAG_UPDATE_CURRENT);

            //item的点击事件
            views.setPendingIntentTemplate(R.id.list_1,pendingIntentTemplate);

            awm.updateAppWidget(appWidgetId, views);
            awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_1);

这里设定了setPendingIntentTemplate。
ps:appwidget里不允许每个item拥有自己的pendingIntent(开销太大)。所以用用另一种方式实现。下面再讲。

RemoteViewsService里的setOnClickFillInIntent

这个类里我们要重写onGetViewFactory函数,它的返回值是RemoteViewsFactory。这个类可以由RemoteViewsService.RemoteViewsFactory得到。
需要重写的关键函数是这个getViewAt

   @Override
        public RemoteViews getViewAt(int position) {
            log("getViewAt, position=" + position);
            RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.layout_adapter);
            views.setTextViewText(R.id.name, mFoods[position]);
            Intent intent = new Intent();
            // 传入点击行的数据
            intent.putExtra("content", mFoods[position]);
            //这里的点击事件会返回到appWidget,用于处理点击事件
            views.setOnClickFillInIntent(R.id.name, intent);
            return views;
        }

这里的setOnClickFillInIntent怎么跟setPendingIntentTemplate联动呢。。进入源码
setOnClickFillInIntent:

public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
            RemoteResponse response = new RemoteResponse();
            response.mFillIntent = fillIntent;
            return response;
        }
        //这里返回一个response,里面有我们在getViewat写的intent
 public void setOnClickResponse(int viewId, @NonNull RemoteResponse response) {
        addAction(new SetOnClickResponse(viewId, response));
    }
    //这个response随风飘摇。
    SetOnClickResponse(int id, RemoteResponse response) {
            this.viewId = id;
            this.mResponse = response;
        }//这个response已经和remoteViews绑一块了
   private void addAction(Action a) {
        if (hasLandscapeAndPortraitLayouts()) {
            throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
                    " layouts cannot be modified. Instead, fully configure the landscape and" +
                    " portrait layouts individually before constructing the combined layout.");
        }
        if (mActions == null) {
            mActions = new ArrayList<>();
        }
        mActions.add(a);
    }
    //这个mActions就是老盆友了。他是一个arraylist对象,里面存着各种apply方法,只要我们调用performApply就能执行每个action对象的apply方法。显然这里把我们对item的操作加了进去。

然后来看看另一个函数setPendingIntentTemplate

  public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
        addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
    }
    //这里也有addAction。。。

RemoteResponse response = null;
int childCount = vg.getChildCount();
for (int i = 0; i < childCount; i++) {
Object tag = vg.getChildAt(i).
getTag(com.android.internal.R.id.fillInIntent);
                    if (tag instanceof RemoteResponse) {
                    response = (RemoteResponse) tag;
                    break;
  }
}
//里面有一段这样的代码。。意思就是获取response。okkk。这样就搭起桥了。

总结:appwidget上对listview的操作挺麻烦的,但看了源码后收获不小。之前一直苦恼这两个怎么联动的。。其实他们两都用一个action数组。。。。最后不要忘记刷新页面。。。不然列表不会有改变的

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值