Android widget 小部件使用指南强化版

news2024/11/18 7:52:44

小部件是主屏幕定制的一个重要方面。您可以将它们视为应用程序最重要的数据和功能的“概览”视图,这些数据和功能可以直接在用户的主屏幕上访问。用户可以在主屏幕面板上移动小部件,如果支持的话,还可以调整它们的大小以根据自己的喜好定制小部件中的信息量。
请添加图片描述

简单UI的小部件

此类小部件通常仅显示关键信息元素,布局简单。小部件属于RemoteViews,常用的控件是支持的,如TextView、Images,但是不支持自定义的控件,具体参考:创建应用微件布局

  1. 声明AppWidgetProviderInfoXML:
    定义了小部件的基本品质。AppWidgetProviderInfo使用单个元素在 XML 资源文件中 定义对象<appwidget-provider>并将其保存在项目的res/xml/文件夹中。
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/all_note_appwidget_layout"
    android:minWidth="150dp"
    android:minHeight="54dp"
    android:previewImage="@drawable/todo_appwidget_preview"
    android:resizeMode="none"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen" />
  • initialLayout:指向定义小部件布局的布局资源
  • previewImage:在选择应用小部件时看到的预览图
  • updatePeriodMillis:小部件更新频率,实际更新不完全按时进行
  • widgetCategory:声明您的小部件是否可以显示在主屏home_screen、锁定屏幕keyguard 或两者上
  1. 在清单中声明一个小部件:AndroidManifest.xml中声明小部件信息
<receiver
    android:name=".widget.AllNoteAppWidgetProvider"
    android:label="@string/all_notes"
    android:exported="true">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <!-- 必须 -->
        <action android:name="com.android.note.widget.UPDATE_ALL_NOTE"/> <!-- 自定义 -->
    </intent-filter>
    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/all_note_appwidget_info" />
</receiver>
  • receiver:指定AppWidgetProvider小部件
  • intent-filter:指定接受 AppWidgetProvider广播。ACTION_APPWIDGET_UPDATE 这是您必须显式声明的唯一广播。根据需要自动 AppWidgetManager 将所有其他小部件广播发送到AppWidgetProvider。还可以自定义其他广播用于更新小部件的数据视图
  • meta-data:android:name指定元数据名称,用于 android.appwidget.provider将数据标识为 AppWidgetProviderInfo描述符。android:resource指定AppWidgetProviderInfo资源位置。
  1. 使用AppWidgetProvider来处理小部件广播
    类AppWidgetProvider扩展 BroadcastReceiver为一个便利类来处理小部件广播,它仅接收与小部件相关的事件广播,例如小部件何时更新、删除、启用和禁用。所以我们重写AppWidgetProvider处理小部件广播并更新小部件以响应小部件生命周期事件。
public class AllNoteAppWidgetProvider extends AppWidgetProvider {

    public static final String UPDATE_ALL_NOTE_COUNT_ACTION = "com.android.note.widget.UPDATE_ALL_NOTE";

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // 当添加相同的小部件时,需要全部遍历更新
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // 接收到自定义的广播,用于做处理后更新小部件
        if (UPDATE_ALL_NOTE_COUNT_ACTION.equals(intent.getAction())) {
            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            final int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context,
                    this.getClass()));
            for (int appWidgetId : appWidgetIds) {
                updateAppWidget(context, appWidgetManager, appWidgetId);
            }
        } else {
            super.onReceive(context, intent);
        }
    }

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        // 小部件的布局,实际就是RemoteViews
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.all_note_appwidget_layout);

        //做数据处理
        NoteDataManager manager = NoteDataManagerImpl.getInstance(context);
        int size = manager.getNotesForPresetFolder(NoteDataManagerImpl.ALL_NOTE_FOLDER).size();
        // 更新视图,作为RemoteViews,只能使用其通过的方法来设置值
        views.setTextViewText(R.id.size, context.getString(R.string.widget_note_count, size));

        // 添加一个跳转应用的意图
        Intent composeIntent = new Intent(context, NoteEditorActivity.class);
        composeIntent.putExtra(NoteEditorPresenter.OPEN_TYPE, 1);
        composeIntent.putExtra(FolderUtil.KEY_ID, 0);
        composeIntent.putExtra(FolderUtil.KEY_FILTER_TYPE, 0);
        PendingIntent composeNoteIntent = PendingIntent.getActivity(context, WidgetUtil.getUniqueCode(),
                composeIntent, PendingIntent.FLAG_IMMUTABLE);
        views.setOnClickPendingIntent(R.id.btn_add, composeNoteIntent);

        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra(Constants.KEY_DEFAULT_VIEW, Constants.TAG_NOTE);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, WidgetUtil.getUniqueCode(),
                intent, PendingIntent.FLAG_MUTABLE);
        views.setOnClickPendingIntent(R.id.header, pendingIntent);
        // 通过 appWidgetManager 更新视图
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    // 用于外部调用更新小部件
    public static void updateCount(Context context) {
        context.sendBroadcast(new Intent(UPDATE_ALL_NOTE_COUNT_ACTION, null, context, AllNoteAppWidgetProvider.class));
    }
}
  • onReceive(Context, Intent):接收来自小部件的广播或者自定义的广播,以便来做一些操作。
  • onUpdate():更新小部件调用此方法。当用户添加小部件时也会调用此方法,因此它应该执行基本设置,例如为 View对象定义事件处理程序或启动作业来加载要在小部件中显示的数据。
  1. 需要更新小部件时的更新方法
AllNoteAppWidgetProvider.updateCount(context);
  1. 效果参考
    在这里插入图片描述

含集合的小部件

集合小部件专门用于显示相同类型的许多元素,例如来自图库应用程序的图片集合、来自新闻应用程序的文章或来自通信应用程序的消息。集合小部件通常专注于两个用例:浏览集合以及将集合的元素打开到其详细视图。集合小部件可以垂直滚动。小部件使用以下视图类型之一呈现数据,这些视图类型称为集合视图:

  • ListView:显示垂直滚动列表中的项目的视图。
  • GridView:显示二维滚动网格中的项目的视图。
  • StackView:堆叠卡片视图(有点像名片盒),用户可以向上或向下轻拂前面的卡片以分别查看上一张或下一张卡片。
  • AdapterViewFlipper:一个由适配器支持的简单动画 ViewAnimator,可以在两个或多个视图之间进行动画处理。一次只显示一个孩子。

由于这些集合视图由适配器支持,因此 Android 框架必须包含额外的架构来支持它们在小部件中的使用。在小部件的上下文中,Adapter被替换为 RemoteViewsFactory,它是界面的薄包装Adapter。当请求集合中的特定项目时,RemoteViewsFactory将创建集合的项目并将其作为对象返回 RemoteViews。要在您的小部件中包含集合视图,请实现RemoteViewsServiceRemoteViewsFactory

关于创建小部件的大部分配置同上操作,关于集合视图的小部件其他操作步骤如下:

  1. 小部件的initialLayout中含有集合视图,如ListView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/app_widget_background"
    android:orientation="vertical"
    android:padding="16dp">

    <ListView
        android:id="@+id/note_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/widget_header"
        android:listSelector="@drawable/transparent_selector"
        android:scrollbars="none" />

</RelativeLayout>
  1. AndroidManifest.xml中声明RemoteViewsService
<service
    android:name=".widget.NoteListWidgetService"
    android:exported="true"
    android:permission="android.permission.BIND_REMOTEVIEWS" />
  • permission:通过在清单文件中使用 权限 声明该服务来执行此操作 BIND_REMOTEVIEWS,可以防止其他应用程序随意访问您的小部件的数据。
  1. 实现RemoteViewsServiceRemoteViewsFactory
public class NoteListWidgetService extends RemoteViewsService {

    private static final String TAG = "NoteListWidgetService";
    public NoteListWidgetService() {
        super();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return super.onBind(intent);
    }

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        // 创建 RemoteViewsFactory
        return new ListViewRemoteViewsFactory(this, intent);
    }

    private static class ListViewRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

        private Context mContext;
        private int mAppWidgetId;
        private ArrayList<NoteItem> mItems = new ArrayList<>();
        private AppWidgetManager mAppWidgetManager;

        public ListViewRemoteViewsFactory(Context context, Intent intent) {
            mContext = context;
            // 获取传递来的 AppWidgetId
            mAppWidgetId =intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
        }

        @Override
        public void onCreate() {
            // 加载数据
            NoteDataManager mManager = NoteDataManagerImpl.getInstance(mContext);
            mItems = (ArrayList)mManager.getNotesForPresetFolder(2);
        }

        @Override
        public void onDataSetChanged() {
            // 更新数据
            NoteDataManager mManager = NoteDataManagerImpl.getInstance(mContext);
            mItems = (ArrayList) mManager.getNotesForPresetFolder(2);

        }

        @Override
        public void onDestroy() {
            mItems.clear();
        }

        @Override
        public int getCount() {
            return Constants.WIDGET_LIST_COUNT;
        }

        @Override
        public RemoteViews getViewAt(int i) {
            int size = mItems.size();
            if (i < size) {
                NoteItem item = mItems.get(i);
                Date date = new Date(item.getLongDate());
                // 创建的ListView项的第一种布局类型
                RemoteViews itemView = new RemoteViews(mContext.getPackageName(), R.layout.widget_listview_item_note);
                itemView.setTextViewText(R.id.note_time, DateUtil.getDisplayNoteTime(mContext, date));
                itemView.setTextViewText(R.id.note_title, NoteUtils.parseContent(mContext, item.getContent(), true));

                // 点击跳转应用,及传递参数,这里没有使用PendingIntent,而是在AppWidgetProvider中
                //通过setPendingIntentTemplate创建了模板,这里只需要Intent即可
                Intent fillInIntent = new Intent();
                fillInIntent.putExtra(FolderUtil.KEY_ID, item.getId());
                fillInIntent.putExtra(NoteEditorPresenter.OPEN_TYPE, NoteEditorPresenter.TYPE_EDIT_NOTE);
                itemView.setOnClickFillInIntent(R.id.listview_linearlayout, fillInIntent);
                return itemView;
            }
            //这里属于ListView项的第二种布局类型
            return new RemoteViews(mContext.getPackageName(), R.layout.app_widget_note_placeholder);
        }

        @Override
        public RemoteViews getLoadingView() {
            return null;
        }

        @Override
        public int getViewTypeCount() {
            // 布局类型的数目
            return 2;
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }
    }
}

关于 RemoteViewsService.RemoteViewsFactory:类似于Adapter的使用

  • onCreate:首次加载,提供数据
  • onDataSetChanged:后续更新,提供新数据
  • onDestroy:清除数据
  • getCount:ListView显示数据的条数
  • getViewAt:返回ListView每项的视图,数据和界面更新的操作都在这里
  • getViewTypeCount:ListView项的视图种类的个数,用于实现不同的布局,至少返回1,否则有数据也不显示,显示正在加载中
  • getItemId:返回当前项的位置
  1. AppWidgetProvider更新时候调用
// 通过 NoteListWidgetService 更新,需要传递appWidgetId
Intent serviceIntent = new Intent(context, NoteListWidgetService.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
        appWidgetIds[i]);
views.setRemoteAdapter(R.id.note_list, serviceIntent);

// 创建 PendingIntent 模板
Intent itemIntent = new Intent(context, NoteEditorActivity.class);
PendingIntent listPendingIntent = PendingIntent.getActivity(context, 1,
        itemIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
views.setPendingIntentTemplate(R.id.note_list, listPendingIntent);

// 通过appWidgetManager更新 ListView,调用 RemoteViewsFactory 的 onDataSetChanged
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds[i], R.id.note_list);
  1. 效果参考:默认显示3条数据,若无,则使用空白占位
    在这里插入图片描述

可配置的小部件

可控制的小部件

CheckBox

Android12更新

Android12微件改进
https://developer.android.google.cn/about/versions/12/features/widgets?hl=zh-cn

相关参考:
https://developer.android.com/guide/topics/appwidgets/overview?hl=zh-cn
https://developer.android.com/develop/ui/views/appwidgets/overview
https://developer.android.com/guide/topics/appwidgets?hl=zh-cn
https://developer.android.com/guide/practices/ui_guidelines/widget_design?hl=zh-cn#anatomy_determining_size
RemoteView
https://android-dot-google-developers.gonglchuangl.net/reference/android/widget/RemoteViews

sample
https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/appwidget

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1047091.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DelayQueue 使用和延时功能源码分析

DelayQueue 延迟队列使用和延时功能源码分析&#xff0c;先看DelayQueue 的使用 目录 1、基本使用 2、延时功能源码分析 3、总结 1、基本使用 想要实现延时功能&#xff0c;需要实现 Delayed 接口&#xff0c;重写 getDelay 方法&#xff0c;在 getDelay 方法里返回延时时间…

关于对象数组的一些方法总结

关于数组对象的一些方法总结 一、判断对象数组是否重复二、复制对象数组 一、判断对象数组是否重复 根据某个属性判断对象数组中&#xff0c;是否含有该属性重复的对象。 let objectArr [{id: 1, name: 2, age: 3}, {id: 2, name: 3, age:24},{id: 1, name: 5, age:6} ]func…

已实现:关于富文本组件库vue2-editor的使用方法以及一些必要的注意事项,特别是设置完富文本以后的回显问题。以及在光标位置插入字符串等

前言 目前常见的基于vue的富文本编辑器有两个&#xff1a;“vue2-editor” 和 “vue-quill-editor” 都是用于Vue.js的富文本编辑器组件&#xff0c;它们具有一些共同的特点&#xff0c;但也有一些区别。 共同点&#xff1a; 1、富文本编辑功能&#xff1a; 两者都提供了富文…

【数据中台建设系列之一】数据中台-元数据管理

本编文章主要介绍数据中台核心模块—元数据模块的一些建设经验分享&#xff0c;供大家一起交流学习。 一、什么是元数据 ​ 元数据可以简单理解为是数据的"数据"&#xff0c;它描述了数据的特征&#xff0c;属性&#xff0c;来源和其他一些数据的基本信息&#xff0…

手机上记录的备忘录内容怎么分享到电脑上查看?

手机已经成为了我们生活中不可或缺的一部分&#xff0c;我们用它来处理琐碎事务&#xff0c;记录生活点滴&#xff0c;手机备忘录就是我们常用的工具之一。但随着工作的需要&#xff0c;我们往往会遇到一个问题&#xff1a;手机上记录的备忘录内容&#xff0c;如何方便地分享到…

基于微信小程序的停车场预约收费小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

数字文创产权版权登记保护多少重要

数字文创产权版权登记保护多少重要 在如今数字化时代&#xff0c;创作者们的作品往往以数字形式存储和传播&#xff0c;这给他们带来了无限的机遇&#xff0c;同时也伴随着一些挑战。在这个环境下&#xff0c;数字文创产权的版权登记显得尤为重要。它不仅能够保护创作者的利益&…

冲刺十五届蓝桥杯P0001阶乘求和

文章目录 题目描述思路分析代码解析 题目描述 思路分析 阶乘是蓝桥杯中常考的知识。 首先我们需要知道 int 和long的最大值是多少。 我们可以知道19的阶乘就已经超过了long的最大值&#xff0c;所以让我们直接计算202320232023&#xff01;的阶乘是不现实的。 所以我们需要…

【逆向】在程序空白区添加Shellcode

目录 硬编码 内存对齐和文件对齐 节表 实战 滴水逆向03-17 #include <windows.h>LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) …

Springboot和Vue:六、ORM介绍+MybatisPlus快速上手

ORM和Mybatis(Plus)介绍 ORM&#xff08;Object Relational Mapping 对象关系映射&#xff09;即映射程序中的对象和数据库中的数据。 MyBatis是一款优秀的数据持久层ORM框架&#xff0c;能够非常灵活地实现动态SQL&#xff0c;可以使用XML或注解来配置和映射原生信息&#xf…

联想笔记本重装系统Win10步骤和详细教程

我们给笔记本电脑重装系统可以解决运行缓慢、出现错误提示等问题&#xff0c;恢复笔记本电脑的流畅运行状态。但是&#xff0c;很多使用联想笔记本电脑的用户不知道重装系统Win10的具体步骤&#xff0c;下面小编给大家介绍关于重装联想笔记本电脑Win10系统的方法步骤吧。 推荐下…

JAMA子刊:最新研究发现腰臀比更能预测死亡

肥胖会危害健康已经称为一个共识&#xff0c;评价肥胖的指标也有多种&#xff0c;例如体质指数&#xff08;BMI)、腰围等。比如BMI大于24为超重&#xff0c;大于28为肥胖&#xff1b;男性腰围≥90cm&#xff0c;女性腰围≥85cm为中心性肥胖。 这些肥胖界值的确定&#xff0c;主…

自制网页。

文章目录 注:代码中图片等素材均来自网络,侵删 20230920_213831 index.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-…

Java应用生产Full GC或者OOM问题如何定位

1 引言 生产应用服务频繁Full GC却无法释放内存&#xff0c;甚至可能OOM&#xff0c;这种情况很有可能是内存泄露或者堆内存分配不足&#xff0c;此时需要dump堆信息来定位问题&#xff0c;查看是哪些地方内存泄漏。 Dump文件也称为内存转储文件或内存快照文件&#xff0c;是…

uni-app:canvas绘制图形3

效果 代码 <template><view><!-- 创建了一个宽度为300像素&#xff0c;高度为200像素的canvas元素。canvas-id属性被设置为"firstCanvas"&#xff0c;可以用来在JavaScript中获取该canvas元素的上下文对象。 --><canvas style"width:200…

mysql面试题3:谈谈你知道的MySQL索引?MySQL中一个表可以创建多少个列索引?MySQL索引有哪几种?他们的优缺点是什么?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:谈谈你知道的MySQL索引? MySQL索引是一种特殊的数据结构,用于加速数据库的查询操作。它通过存储列值和对应记录的指针,可以快速定位到满足查询…

计算机竞赛 深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习&#xff1f;5.1.2 为什么要迁移学习&#xff1f; 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

计算机网络-网络层总结

目录 网络层提供的两种服务 1、分组转发和路由选择 2、网络层提供的两种服务 网际协议IP 1、虚拟互联网 2、网际协议IP 3、异构网络互连 4、IPv4地址 概述 4.1分类编址的IPv4地址 4.2划分子网的IPv4地址 4.3无分类编址的IPv4地址 4.4IPv4地址的应用规划 4.5MAC地址…

使用 Spring Data Redis 访问 Redis 的 Spring Boot 应用

使用 Spring Data Redis 访问 Redis 的 Spring Boot 应用 Redis是一个高性能的内存键值存储数据库&#xff0c;常用于缓存、会话管理和消息队列等场景。Spring Boot通过Spring Data Redis提供了方便的方式来与Redis进行交互。在本文中&#xff0c;我们将详细介绍如何使用Sprin…

nodejs+vue 经典建筑网站elementui

第1章 项目概述 1 1.1 问题描述 1 1.2 项目目标 1 1.3 项目适用范围 2 1.4 项目应遵守的规范与标准 2 1.5 涉众 2 具有功能强大、支持跨平台、运行速度快、安全性高、成本低等优点。而对于后者我们使用 来完成它&#xff0c;使其网页功能完备&#xff0c;界面友好、易开发、易…