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. django-模板初探

    一般而言,我们在视图函数中处理各种业务逻辑之后,应该返回一个 HttpResponse 对象.而 HttpResponse 对象的第一个参数接受字符串或者是迭代器,作为响应报文的主体.但是这意味着我们 ...

  2. android的AsyncTask.get()方法会阻塞UI线程

    AsyncTask.get()方法, 是有阻塞UI的能力的.

  3. bzoj 2809 可并堆维护子树信息

    对于每个节点,要在其子树中选尽量多的节点,并且节点的权值和小于一个定值. 建立大根堆,每个节点从儿子节点合并,并弹出最大值直到和满足要求. /***************************** ...

  4. 简表-Java-Echart报表介绍

    Java后台报表尝试了很多,最终发现了一款,而且是开源的,简表地址:http://www.jatools.com/jor/.问题的引入:该报表支持嵌套,钻去,应对excel类似的报表,足够了.但是,报 ...

  5. shell脚本调用C语言之字符串切分函数——strtok

    今天上午在写一个需求,要求的比较急,要求当天完成,我大致分析了一下,可以采用从shell脚本中插入一连串的日期,通过调用proc生成的可执行文件,将日期传入后台数据库,在数据库中进行计算.需要切分日期 ...

  6. 使用Docker中国官方镜像的加速地址

    vi /etc/docker/daemon.json # 添加如下内容 { "registry-mirrors": ["https://registry.docker-c ...

  7. spring cloud 学习(5) - config server

    分布式环境下的统一配置框架,已经有不少了,比如百度的disconf,阿里的diamand.今天来看下spring cloud对应的解决方案: 如上图,从架构上就可以看出与disconf之类的有很大不同 ...

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

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

  9. ARM JTAG 20P to Cortex JTAG 10P

  10. 聚币网API使用教程 demo

    原文 http://30daydo.com/article/181 目前还在完善,等功能完善了,就更新到csdn. 更新 2017-05-27 官方有API的文档,可是这个文档就像一个草稿一样,两个基 ...