zoukankan      html  css  js  c++  java
  • Android UI组件----AppWidget控件入门详解

    Widget引入

    我们可以把Widget理解成放置在桌面上的小组件(挂件),有了Widget,我们可以很方便地直接在桌面上进行各种操作,例如播放音乐。

    当我们长按桌面时,可以看到Widget选项,如下图所示:

    点击上图中箭头处的widgets图标,会出现如下界面:(都是widget)

    长按上图中的任意一个widget,就可以将其放到桌面上。

    Widget的使用

    Widget的实现思路

    (1)在AndroidManifest中声明AppWidget;

    (2)在xml目录中定义AppWidget的配置文件;

    (3)在layout目录中定义Widget的布局文件;

    (4)新建一个类,继承AppWidgetProvider类,实现具体的widget业务逻辑。

    我们需要新建一个类,继承AppWidgetProvider。点开AppWidgetProvider,发现AppWidgetProvider竟然是继承自BroadcastReceiver

    为什么Widget是一个广播接收器呢?我们知道,BroadcastReceiver类中有一个onReceive方法,用来接收广播。当我们在桌面挂件上去做操作时,必然引起应用的改变,这就涉及到挂件和应用之间的通信,此时用广播来通信是再好不过了

    Widget的具体使用步骤

    (1)新建一个类TestWidget.java,继承AppWidgetProvider:
    TestWidget.java:

    import android.appwidget.AppWidgetProvider;
    import android.content.Context;
    import android.content.Intent;
    
    /**
     * Created by smyhvae on 2016/9/7.
     */
    
    public class TestWidget extends AppWidgetProvider{
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
        }
    }
    

    (2)因为Widget是一个广播接收器,所以我们需要在清单文件中注册:

            <!-- 声明widget对应的AppWidgetProvider -->
            <receiver android:name=".TestWidget">
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
                </intent-filter>
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@layout/widget_setting"/>
            </receiver>
    

    04行:action是过滤条件,用来过滤行为,监测widget的更新。
    08行:android:resource指定了widget的配置。我们知道,属性在清单文件中是用来存储数据的。

    (3)layout文件夹中新建文件widget_setting.xml:(widget的配置文件)
    setting_widget.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:initialLayout="@layout/layout_widget"
                        android:minHeight="140dp"
                        android:minWidth="140dp"
                        android:previewImage="@mipmap/ic_launcher"
                        android:updatePeriodMillis="20000"
                        android:widgetCategory="home_screen"
        >
    </appwidget-provider>
    

    08行: android:initialLayout 指定了widget的布局。
    09行:android:updatePeriodMillis 指定更新的时间周期
    10行: android:widgetCategory="home_screen" 将widget显示在主屏幕上(也可以显示在锁屏上)

    (4)layout文件夹中新建文件layout_widget.xml:(widget的布局)
    layout_widget.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Large Text"
            android:textAppearance="?android:attr/textAppearanceLarge"/>
    
        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="New Button"/>
    
    </LinearLayout>
    

    到此,程序就可以跑起来了。运行程序之后,长按桌面,点开"Widget"按钮,可以看到我们刚刚设计出的widget:

    长按上图中的箭头处,就可以将我们设计出的widget拖放到桌面上了:

    Widget的点击和更新【重要】

    我们知道,TestWidget.java继承自AppWidgetProvider,而AppWidgetProvider在继承BroadcastReceiver之后,重写onReceive方法,然后还自定义了很多方法:

    上图中,包含了被删除时、被禁用时、被启用时、被更新时等各种方法。尤其重要的是onReceive()方法和onUpDate()方法。
    当小部件被改变时(比如被安装到桌面),系统会发送一个更新的广播(上图红框部分所示)。我们在setting_widget.xml中设置了widget的更新频率,这个也会调用更新。

    有人可能会问,我开机之后,天气等widget为何不更新了?这是因为进程被杀死了,那我们只能把这个控件先移除,然后再装上,此时应用会发update更新的广播。

    当需要做widget的点击和更新时,我们需要在需要重写onUpdate()方法,用来发送广播。当程序初始化的时候,系统就会调用onUpdate()方法。

    onUpdate()方法中的代码如下:

        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            super.onUpdate(context, appWidgetManager, appWidgetIds);
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//需要构造一个RemoteViews
    
            Intent intent = new Intent();
            intent.setClass(context, TestWidget.class); //通过intent把广播发给TestWidget本身,TestWidget接受到广播之后,会调用onReceive()方法进而刷新界面。
            intent.setAction(WIDGET_BTN_ACTION);
    
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    
            remoteViews.setOnClickPendingIntent(R.id.widget_btn, pendingIntent);//控件btn_widget的点击事件:点击按钮时,会发一个带action的广播。
    
            appWidgetManager.updateAppWidget(appWidgetIds, remoteViews); //点击完了之后,记得更新一下。
    
        }
    

    代码解释:
    首先需要new一个RemoteViews,构造方法里需要传递两个参数,一个是包名(context.getPacakgeName),一个是布局文件(layout_widget)。
    然后通过remoteViews.setOnClickPendingIntent()设置按钮的点击事件。setOnClickPendingIntent()中需要传递两个参数:一个是id(比如需要被点击的button),一个是PendingIntent。PendingIntent是未来的意图。
    于是我们需要事先构造一个PendingIntent,这个需要通过 PendingIntent.getBroadcast()来构造。getBroadcast()方法中需要传递四个参数,其中有一个是Intent。
    于是我们需要构造一个Intent。在intent里发送广播,并设置Action。
    按钮点击完了之后,记得调用appWidgetManager.updateAppWidget(int[] appWidgetIds, RemoteViews views)方法更新一下,第一个参数就是onUpdate方法中的参数,代表的是所有的控件。

    在onUpdate()方法中通过intent发送按钮点击时间的广播之后,我们需要在onReceive()方法中进行广播的接收。
    onReceive()方法中的代码如下:

        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
    
            if (intent != null && TextUtils.equals(intent.getAction(), WIDGET_BTN_ACTION)) { //当intent不为空,且action匹配成功时,就接收广播,然后点击事件成功
                Log.i(WIDGET_BTN_ACTION, "is clicked");
                //接下来开始做点击事件里面的内容
                RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】构造一个RemoteViews
                remoteViews.setTextViewText(R.id.widget_tv, "be clicked");
                remoteViews.setTextColor(R.id.widget_tv, Color.RED);
    
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 单例模式
                ComponentName componentName = new ComponentName(context, TestWidget.class);
                appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,记得更新一下
            }
        }
    

    代码解释:
    当intent的action匹配成功时,开始执行做点击时间之后的setText,不过这里需要重新new 一个 RemoteViews,而不能共用onUpdate()方法中的RemoteViews(这是一个很大的坑)
    执行完点击事件之后的setText之后,记得调用appWidgetManager.updateAppWidget(ComponentName, RemoteViews)方法,第一个参数为组件名,需要我们自己new一下,第二个参数很好解释。

    综合来说,TestWidget.java的完整版代码如下:
    Testwidget.java:

    
    import android.app.PendingIntent;
    import android.appwidget.AppWidgetManager;
    import android.appwidget.AppWidgetProvider;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Color;
    import android.text.TextUtils;
    import android.util.Log;
    import android.widget.RemoteViews;
    
    /**
     * Created by smyhvae on 2016/9/7.
     */
    public class TestWidget extends AppWidgetProvider {
    
        public static final String WIDGET_BTN_ACTION = "widget_btn_action";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
    
            if (intent != null && TextUtils.equals(intent.getAction(), WIDGET_BTN_ACTION)) { //当intent不为空,且action匹配成功时,就接收广播,然后点击事件成功
                Log.i(WIDGET_BTN_ACTION, "is clicked");
                //接下来开始做点击事件里面的内容
                RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//注意:需要【重新】构造一个RemoteViews
                remoteViews.setTextViewText(R.id.widget_tv, "be clicked");
                remoteViews.setTextColor(R.id.widget_tv, Color.RED);
    
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);// 单例模式
                ComponentName componentName = new ComponentName(context, TestWidget.class);
                appWidgetManager.updateAppWidget(componentName, remoteViews);//setText之后,记得更新一下
            }
        }
    
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            super.onUpdate(context, appWidgetManager, appWidgetIds);
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_widget);//需要构造一个RemoteViews
    
            Intent intent = new Intent();
            intent.setClass(context, TestWidget.class); //通过intent把广播发给TestWidget本身,TestWidget接受到广播之后,会调用onReceive()方法进而刷新界面。
            intent.setAction(WIDGET_BTN_ACTION);
    
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
    
            remoteViews.setOnClickPendingIntent(R.id.widget_btn, pendingIntent);//控件btn_widget的点击事件:点击按钮时,会发一个带action的广播。
    
            appWidgetManager.updateAppWidget(appWidgetIds, remoteViews); //点击完了之后,记得更新一下。
    
        }
    }
    

    运行之后,把widget拖到桌面上,效果如下:

    点击按钮后,效果如下:

    工程文件:(Android Studio 2.1)http://download.csdn.net/detail/smyhvae/9624840

    当然,widget还有很多其他的用途。比如:

    • 与Service进行通信
    • widget控件的交互方法。
    • 如何做一个桌面播放器Widget

    参考链接:

    Android 之窗口小部件详解--App Widget:http://www.cnblogs.com/skywang12345/p/3158310.html

  • 相关阅读:
    浅谈MyBatis-Plus学习之条件构造器 EntityWrapper
    浅谈MyBatis-Plus学习之插件扩展
    [XSS防御]HttpOnly之四两拨千斤
    [PHP防火墙]输入内容存在危险字符,安全起见,已被本站拦截
    [思路笔记]WEB安全之漏洞挖掘
    通过TleChat插件一键Getshell
    云服务器上安装MSF环境
    (vshadow)Volume Shadow在渗透测试中的利用
    一个帖子csrf的例子
    yuyuecms 1.2文件删除漏洞
  • 原文地址:https://www.cnblogs.com/qianguyihao/p/5851780.html
Copyright © 2011-2022 走看看