Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

AppWidgetProvider 桌面插件 Widget 广播


目录

简介

Demo

效果:



AppWidgetProvider是android中提供的用于实现桌面小工具的类,其本质是一个广播,即BroadcastReceiver。

public class AppWidgetProvider extends BroadcastReceiver 

所以,在实际的使用中,把AppWidgetProvider当成一个BroadcastReceiver就可以了。

App Widgets是一个显示在别的application中(比如显示在桌面程序)的微型application views,并且定期接受更新。这个views在用户界面被叫Widgets,你可以发布一个自己应用的Widget。Widget的application称为App Widget host。

App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates. These views are referred to as Widgets in the user interface, and you can publish one with an App Widget provider. An application component that is able to hold other App Widgets is called an App Widget host.

AppWidget 框架类

  • AppWidgetProvider :继承自 BroadcastRecevier , 在 AppWidget 应用 update、enable、disable 和 delete 时接收通知。其中,onUpdate、onReceive 是最常用到的方法,它们接收更新通知。
  • AppWidgetProvderInfo:描述 AppWidget 的大小、更新频率和初始界面等信息,以XML 文件形式存在于应用的 res/xml/目录下。
  • AppWidgetManger :负责管理 AppWidget ,向 AppwidgetProvider 发送通知。
  • RemoteViews :一个可以在其他应用进程中运行的类,向 AppWidgetProvider 发送通知。

AppWidgetProvider中的几个回调方法:onEnabled,onDisabled,onDeleted,onUpdated会自动被其onReceive方法在合适的时间调用,确切来说是,当广播到来以后,AppWidgetProvider会自动根据广播的action通过onReceive方法来自动派发广播,也就是调用上述几个方法。

AppWidgetManger 常用 API

  • bindAppWidgetId(int appWidgetId, ComponentName provider):通过给定的 ComponentName 绑定appWidgetId
  • getAppWidgetIds(ComponentName provider):通过给定的 ComponentName 获取AppWidgetId
  • getAppWidgetInfo(int appWidgetId):通过 AppWidgetId 获取 AppWidget 信息
  • getInstalledProviders():返回一个 List<AppWidgetProviderInfo> 的信息
  • getInstance(Context context):获取 AppWidgetManger 实例(静态方法)
  • updateAppWidget(int[] appWidgetIds, RemoteViews views):通过 appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新vAppWidget 组件
  • updateAppWidget(ComponentName provider, RemoteViews views):通过 ComponentName 对传进来的 RemoeteView 进行修改,并重新刷新AppWidget 组件
  • updateAppWidget(int appWidgetId, RemoteViews views):通过 appWidgetId 对传进来的 RemoteView 进行修改,并重新刷新AppWidget 组件

开发步骤

定义小工具界面

在res/layout/下新建一个xml文件,在里面设计小工具要做成什么样子。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#300f"> <ImageView
android:id="@+id/iv_icon"
android:layout_width="70dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:src="@drawable/icon"/> <TextView
android:id="@+id/tv_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:gravity="center"
android:padding="3dp"
android:singleLine="true"
android:text="时间 2018.11.16 13.40.49"
android:textColor="#a000"
android:textSize="11sp"/> <TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/tv_data"
android:layout_toRightOf="@+id/iv_icon"
android:gravity="center"
android:text="内容"
android:textColor="#fff"
android:textSize="15sp"/> </RelativeLayout>

定义小工具配置信息

在res/xml/下新建my_app_widget_info.xml,名称随意,添加如下内容:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/my_app_widget"
android:initialLayout="@layout/my_app_widget"
android:minHeight="40dp"
android:minWidth="110dp"
android:previewImage="@drawable/icon"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen|keyguard"/>

配置项:

  • initialKeyguardLayout:锁屏时插件使用的布局
  • initialLayout:桌面时插件使用的布局
  • minWidth、minHeight:小部件的最小宽高,计算公式为:(70*N)-30,N为打算在屏幕上占几格
  • previewImage:小部件的列表中的预览图
  • resizeMode:调整size模式,即是否可以在水平、竖直方向伸缩
  • updatePeriodMillis:更新的周期,单位为毫秒,0表示没有更新周期;系统为了省电,默认是30分钟更新一次,如果你设置的值比30分钟小,系统也是30分钟才会更新一次。如果需要频繁更新 Widget,需要自己起一个service进行更新。
  • widgetCategory:插件模式(可添加的位置),锁屏或(和)桌面

定义小工具类

继承AppWidgetProvider,重写所需要的方法

public class MyWidget extends AppWidgetProvider {
public static final String ACTION_MYWIDGET_ONCLICK = "com.bqt.test.mywidget.onclick"; @SuppressLint("UnsafeProtectedBroadcastReceiver")
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.i("bqt", "【onReceive,其他所有回调方法都是由它调用的】");
//这里判断是自己的action,做自己的事情,比如小工具被点击了要干啥
if (ACTION_MYWIDGET_ONCLICK.equals(intent.getAction())) {
Toast.makeText(context, "什么是最重要的呢?\n 时间!争分夺秒!", Toast.LENGTH_LONG).show();
}
} @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
//根据 updatePeriodMillis 定义的时间定期调用该函数,此外当用户添加 Widget 时也会调用该函数,可以在这里进行必要的初始化操作。
Log.i("bqt", "【onUpdate,当插件内容更新函数时调用,最重要的方法】" + Arrays.toString(appWidgetIds));
for (int appWidgetId : appWidgetIds) {
String text = context.getSharedPreferences("MyWidget", Context.MODE_PRIVATE).getString("MyWidgetText", "");
RemoteViews remoteViews = Utils.getRemoteViews(context, text);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
} @Override
public void onEnabled(Context context) {
Log.i("bqt", "【onEnabled,当 Widget 第一次被添加时调用】");
//例如用户添加了两个你的 Widget,那么只有在添加第一个 Widget 时该方法会被调用,该方法适合执行你所有 Widgets 只需进行一次的操作
} @Override
public void onDisabled(Context context) {
Log.i("bqt", "【onDisabled,当你的最后一个 Widget 被删除时调用】");//该方法适合用来清理之前在 onEnabled() 中进行的操作
} @Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
Log.i("bqt", "【onDeleted,当 Widget 被删除时调用】" + Arrays.toString(appWidgetIds));
} @Override
public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
super.onRestored(context, oldWidgetIds, newWidgetIds);
Log.i("bqt", "【onRestored,被还原是调用】旧" + Arrays.toString(oldWidgetIds) + ",新" + Arrays.toString(newWidgetIds));
} @Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
Log.i("bqt", "【onAppWidgetOptionsChanged,当 Widget 第一次被添加或者大小发生变化时调用】");
}
}

声明小工具类

<receiver android:name=".MyWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/><!--必须添加的action,否则不会出现在小部件的列表中-->
<action android:name="com.bqt.test.mywidget.onclick"/><!--自定义的action,用于在点击小部件上时发送的广播Action-->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/><!--下面这几个都是不必须的-->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS"/>
<action android:name="android.appwidget.action.APPWIDGET_RESTORED"/>
<action android:name="android.appwidget.action.APPWIDGET_DELETED"/>
</intent-filter> <meta-data
android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info"/>
</receiver>

与 Activity 的交互

public class EditActivity extends Activity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout linearLayout = getContentView();
setContentView(linearLayout);
new Handler().postDelayed(this::showSoftInput, 100);
} @NonNull
private LinearLayout getContentView() {
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL); EditText editText = new EditText(this);
editText.setHint("请输入小工具中显示的内容");
editText.setText(getIntent() != null ? getIntent().getStringExtra("text") : "");
editText.setLines(3);
editText.setGravity(Gravity.CENTER);
editText.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
linearLayout.addView(editText); Button button = new Button(this);
button.setText("保存");
button.setOnClickListener(v -> {
updateAppWidget(editText.getText().toString());
finish();
});
linearLayout.addView(button); return linearLayout;
} private void updateAppWidget(String text) {
ComponentName componentName = new ComponentName(this, MyWidget.class);
getSharedPreferences("MyWidget", Context.MODE_PRIVATE).edit().putString("MyWidgetText", text).apply();
RemoteViews remoteViews = Utils.getRemoteViews(this, text);
AppWidgetManager.getInstance(this).updateAppWidget(componentName, remoteViews);
} private void showSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
}
}
}

工具类

public class Utils {

    public static RemoteViews getRemoteViews(Context context, String text) {
SpannableString textSpannableString = Utils.getSpannableString(context, text, Color.WHITE, 15);
String date = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date());
SpannableString dateSpannableString = Utils.getSpannableString(context, date, Color.DKGRAY, 12); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
remoteViews.setTextViewText(R.id.tv_data, dateSpannableString);//时间
remoteViews.setTextViewText(R.id.tv_text, textSpannableString);//内容 Intent intent = new Intent(context, EditActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
intent.putExtra("text", text);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.iv_icon, pendingIntent); Intent actionIntent = new Intent(context, MyWidget.class);//显示意图
actionIntent.setAction(MyWidget.ACTION_MYWIDGET_ONCLICK);
//actionIntent.setPackage(context.getPackageName());//隐式意图必须设置Package,实际测试发现,如果使用隐式意图,在应用被杀掉时不响应广播
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, actionIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.tv_text, pIntent); return remoteViews;
} public static SpannableString getSpannableString(Context context, String source, int color, int size) {
SpannableString mSpannableString = new SpannableString(source);
int dpValue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, context.getResources().getDisplayMetrics());
String firstLine = source.contains("\n") ? source.substring(0, source.indexOf("\n")) : source; //第一行的样式
ForegroundColorSpan colorSpan = new ForegroundColorSpan(color);//颜色
mSpannableString.setSpan(colorSpan, 0, firstLine.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(dpValue);//大小
mSpannableString.setSpan(absoluteSizeSpan, 0, firstLine.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //其他行的样式
if (source.contains("\n")) {
String otherLine = source.substring(source.indexOf("\n"));
if (otherLine.length() > 0) {
ForegroundColorSpan colorSpan2 = new ForegroundColorSpan(Color.YELLOW);//颜色
mSpannableString.setSpan(colorSpan2, firstLine.length(), source.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
AbsoluteSizeSpan absoluteSizeSpan2 = new AbsoluteSizeSpan((int) (0.8f * dpValue));//大小
mSpannableString.setSpan(absoluteSizeSpan2, firstLine.length(), source.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return mSpannableString;
}
}

2018-11-15

AppWidgetProvider 桌面插件 Widget 广播 MD的更多相关文章

  1. 使用 Android Studio 开发 widget 安卓桌面插件

    •What AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序: 这么说可能有点抽象,看图: 像这种,桌面上的天气.时钟.搜索框等等,都属于 APP Wi ...

  2. Android桌面小插件——Widget

    Android桌面小插件--Widget 效果图 实现 1. 创建Widget类 创建一个Widget类,并实现页面创建的时候,就实现显示时间 package com.kongqw.kqwwidget ...

  3. iOS桌面小插件 Widget Extension

    iOS桌面小插件 Widget Extension 这个插件时iOS14以后才出现的,基于SwiftUI 旧项目新建时可能一堆错误,其中一个时要把插件target 开发sdk版本设置为14.0以上 新 ...

  4. $.widget 编写jQueryUI插件(widget)

    转自:MainTao: 编写jQueryUI插件(widget) 使用jQueryUI的widget来写插件,相比于基本的jquery插件有一些好处: * 方便实现继承,代码重用 * 默认是单例 * ...

  5. Android开发之Shortcuts, LiveFolder, Widget

    2013-07-05 桌面组件包括:快捷方式(Shortcuts),实时文件夹(Live Folder),桌面插件(Widget).   快捷方式用于启动应用程序的某个组件,例如Activity, S ...

  6. 强烈推荐:Android史上最强大的自定义任务软件Tasker

    强烈推荐:Android史上最强大的自定义任务软件Taskerhttp://bbs.mumayi.com/thread-28387-1-1.html(出处: 木蚂蚁手机乐园) Android上的Tas ...

  7. Tasker 中文版(4.6u2)

    http://www.anzhi.com/soft_1868913.html Tasker绝对称得上是Android系统的神器之一,与Auto Memory Manager不同,Tasker不是加速型 ...

  8. Tasker, Android系统增强神器

    Tasker是一个让系统根据用户定制的”配置文件”(Profiles),在特定的”背景”下(Contexts),执行指定”任务”(Tasks)的软件, 除此之外,它还提供”可供点击”的(Clickab ...

  9. 给你的app添加桌面widget

    首先,什么是桌面widget,桌面widget是一种桌面插件,如下图: 这种类型的控件叫做widget,一般长按桌面会弹出一个界面让你选择控件,选择完了拖到桌面就能使用了. 下面我们为这个app来添加 ...

随机推荐

  1. C# asp.net 抓取需要登录的网页内容 抓取asp.net登录验证的网站

    private void btnASPNET_Click(object sender, EventArgs e)        {            Dictionary<string, s ...

  2. 【dfs】BZOJ1703-[Usaco2007 Mar]Ranking the Cows 奶牛排名

    [题目大意] 农夫约翰有N(1≤N≤1000)头奶牛,每一头奶牛都有一个确定的独一无二的正整数产奶率.约翰想要让这些奶牛按产奶率从高到低排序,约翰已经比较了M(1≤M≤10000)对奶牛的产奶率,但他 ...

  3. Slickflow.NET 开源工作流引擎高级开发(三) -- 并行分支容器与会签工作流模式的组合

    前言:  流程引擎的核心功能是负责解析流程定义XML和流转,业务环节的不断积累,让人们不断总结和抽象出一些模式,这些模式统称为工作流模式(Workflow Pattern).本文的重点就是介绍一种常见 ...

  4. HDU 4790 Just Random (2013成都J题)

    Just Random Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  5. 推荐13个.Net开源的网络爬虫

    1:.Net开源的跨平台爬虫框架 DotnetSpider Star:430 DotnetSpider这是国人开源的一个跨平台.高性能.轻量级的爬虫软件,采用 C# 开发.目前是.Net开源爬虫最为优 ...

  6. CF330 C. Purification 认真想后就成水题了

    C. Purification time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  7. 多个按钮触发同一个Bootstrap自适应模态窗口

    在项目中可能会面对这样的一个场景: 界面上有多个按钮,我们希望点击这些按钮弹出同一个模态窗口,但希望模态窗口的内容是动态生成的,即,点击每个按钮弹出的模态窗口内容不同. 通常情况下,一个按钮对应一个模 ...

  8. SpringBoot打jar包问题

    原文:https://jingyan.baidu.com/article/6f2f55a11d6e09b5b93e6c9e.html 当你使用springBoot进行打包的时候,这篇经验会帮助到你的. ...

  9. NSNotification的使用(对观察者模式最通俗、易懂的讲解)

    这是一个观察者模式. 首先在你需要监听的类中加入观察者: - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString ...

  10. Traceroute(路由追踪)的原理及实现

    现实世界中的网络是由无数的计算机和路由器组成的一张的大网,应用的数据包在发送到服务器之前都要经过层层的路由转发.而Traceroute是一种常规的网络分析工具,用来定位到目标主机之间的所有路由器 原理 ...