Android 之App Widget

1 App Widget简介

应用微件是可以嵌入其他应用(如主屏幕)并接收定期更新的微型应用视图。这些视图称为界面中的微件,您可以使用应用微件提供程序发布微件。能够容纳其他应用微件的应用组件称为应用微件托管应用。下面的屏幕截图显示了闹钟微件。

 2 App Widg

要创建应用微件,您需要:

AppWidgetProviderInfo 对象

描述应用微件的元数据,如应用微件的布局、更新频率和 AppWidgetProvider 类。此对象应在 XML 中定义。

AppWidgetProvider 类实现

定义允许您基于广播事件以编程方式与应用微件连接的基本方法。通过它,您会在更新、启用、停用和删除应用微件时收到广播。

桌面小部件的开发步骤

1.在应用的 AndroidManifest.xml 文件中声明AppWidgetProvider类。

例如:
  <receiver android:name=".ExampleAppWidgetProvider" >
         <intent-filter>
   <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
   <action android:name="com.skywang.widget.UPDATE_ALL"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
   android:resource="@xml/example_appwidget_info" />
     </receiver>

<receiver>元素需要android:name属性,该属性指定应用微件使用的AppWidgetProvider 。
<intent-filter>元素必须包含一个具有 android:name属性的<action>元素。此属性指定AppWidgetProvider接受 ACTION_APPWIDGET_UPDATE广播。这是您必须明确声明的唯一一项广播。AppWidgetManager 会根据需要自动将其他所有应用微件广播发送到AppWidgetProvider。
<meta-data>元素指定AppWidgetProviderInfo资源,并且需要以下属性:
android: name-指定元数据名称。使用android.appwidget.provider将数据标识为AppWidgetProviderInfo 描述符。
android :resource-指定AppWidgetProviderInfo资源位置。

2. 编辑AppWidgetProviderInfo对应的资源文件

  <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="40dp"
        android:minHeight="40dp"
        android:updatePeriodMillis="86400000"
        android:previewImage="@drawable/preview"
        android:initialLayout="@layout/example_appwidget"
        android:configure="com.example.android.ExampleAppWidgetConfigure"
        android:resizeMode="horizontal|vertical"
        android:widgetCategory="home_screen">
    </appwidget-provider>
    
    <!--
    android:minWidth : 最小宽度
    android:minHeight : 最小高度
    android:updatePeriodMillis : 更新widget的时间间隔(ms),"86400000"为1个小时
    android:previewImage : 预览图片
    android:initialLayout : 加载到桌面时对应的布局文件
    android:resizeMode : widget可以被拉伸的方向。horizontal表示可以水平拉伸,vertical表示可以竖直拉伸
    android:widgetCategory : widget可以被显示的位置。home_screen表示可以将widget添加到桌面,keyguard表示widget可以被添加到锁屏界面。
    android:configure : 定义要在用户添加应用微件时启动以便用户配置应用微件属性的 Activity
     -->
    

3. 编辑example_appwidget.xml等资源文件

新建layout/example_appwidget.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" >
  
    <LinearLayout 
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"
        android:layout_gravity="center" 
        android:orientation="horizontal" >
        
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="HomeScreen Widget" />    
        
        <Button
            android:id="@+id/btn_show"
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="Show" />
    </LinearLayout> 
        
    <ImageView
        android:id="@+id/iv_show"
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" 
        android:layout_gravity="center"/> 
        
</LinearLayout>
4. 编辑ExampleAppWidgetProvider.java


public class ExampleAppWidgetProvider extends AppWidgetProvider {
   private static final String TAG = "ExampleAppWidgetProvider";

   private boolean DEBUG = false; 
    // 启动ExampleAppWidgetService服务对应的action
    private final Intent EXAMPLE_SERVICE_INTENT = 
          new Intent("android.appwidget.action.EXAMPLE_APP_WIDGET_SERVICE");
    // 更新 widget 的广播对应的action
   private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";
    // 保存 widget 的id的HashSet,每新建一个 widget 都会为该 widget 分配一个 id。
   private static Set idsSet = new HashSet();
   // 按钮信息
    private static final int BUTTON_SHOW = 1;
   // 图片数组
    private static final int[] ARR_IMAGES = {
         R.drawable.ic_launcher,
         R.drawable.sample_0,
         R.drawable.sample_1,
         R.drawable.sample_2,
          
    };
    
   // onUpdate() 在更新 widget 时,被执行,
   @Override
   public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
      Log.d(TAG, "onUpdate(): appWidgetIds.length="+appWidgetIds.length);

      // 每次 widget 被创建时,对应的将widget的id添加到set中
      for (int appWidgetId : appWidgetIds) {
         idsSet.add(Integer.valueOf(appWidgetId));
      }
      prtSet();
   }
   
    // 当 widget 被初次添加 或者 当 widget 的大小被改变时,被调用 
    @Override  
    public void onAppWidgetOptionsChanged(Context context,  
            AppWidgetManager appWidgetManager, int appWidgetId,  
            Bundle newOptions) {
       Log.d(TAG, "onAppWidgetOptionsChanged");
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId,  
                newOptions);  
    }  
    
    // widget被删除时调用  
    @Override  
    public void onDeleted(Context context, int[] appWidgetIds) {  
      Log.d(TAG, "onDeleted(): appWidgetIds.length="+appWidgetIds.length);

      // 当 widget 被删除时,对应的删除set中保存的widget的id
      for (int appWidgetId : appWidgetIds) {
         idsSet.remove(Integer.valueOf(appWidgetId));
      }
      prtSet();
      
        super.onDeleted(context, appWidgetIds);  
    }

    // 第一个widget被创建时调用  
    @Override  
    public void onEnabled(Context context) {  
       Log.d(TAG, "onEnabled");
       // 在第一个 widget 被创建时,开启服务
       context.startService(EXAMPLE_SERVICE_INTENT);
       
        super.onEnabled(context);  
    }  
    
    // 最后一个widget被删除时调用  
    @Override  
    public void onDisabled(Context context) {  
       Log.d(TAG, "onDisabled");

       // 在最后一个 widget 被删除时,终止服务
       context.stopService(EXAMPLE_SERVICE_INTENT);

        super.onDisabled(context);  
    }
    
    
    // 接收广播的回调函数
    @Override  
    public void onReceive(Context context, Intent intent) {  

        final String action = intent.getAction();
        Log.d(TAG, "OnReceive:Action: " + action);
        if (ACTION_UPDATE_ALL.equals(action)) {
           // “更新”广播
           updateAllAppWidgets(context, AppWidgetManager.getInstance(context), idsSet);
       } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
          // “按钮点击”广播
           Uri data = intent.getData();
           int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
           if (buttonId == BUTTON_SHOW) {
              Log.d(TAG, "Button wifi clicked");
              Toast.makeText(context, "Button Clicked", Toast.LENGTH_SHORT).show();
           }
       }
        
        super.onReceive(context, intent);  
    }  

    // 更新所有的 widget 
    private void updateAllAppWidgets(Context context, AppWidgetManager appWidgetManager, Set set) {

      Log.d(TAG, "updateAllAppWidgets(): size="+set.size());
      
      // widget 的id
       int appID;
       // 迭代器,用于遍历所有保存的widget的id
       Iterator it = set.iterator();

       while (it.hasNext()) {
          appID = ((Integer)it.next()).intValue();    
          // 随机获取一张图片
          int index = (new java.util.Random().nextInt(ARR_IMAGES.length));
          
          if (DEBUG) Log.d(TAG, "onUpdate(): index="+index);        
          // 获取 example_appwidget.xml 对应的RemoteViews        
          RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
          
          // 设置显示图片
          remoteView.setImageViewResource(R.id.iv_show, ARR_IMAGES[index]);
          
          // 设置点击按钮对应的PendingIntent:即点击按钮时,发送广播。
          remoteView.setOnClickPendingIntent(R.id.btn_show, getPendingIntent(context,
                    BUTTON_SHOW));

          // 更新 widget
          appWidgetManager.updateAppWidget(appID, remoteView);      
       }      
   }

    private PendingIntent getPendingIntent(Context context, int buttonId) {
        Intent intent = new Intent();
        intent.setClass(context, ExampleAppWidgetProvider.class);
        intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
        intent.setData(Uri.parse("custom:" + buttonId));
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0 );
        return pi;
    }

    // 调试用:遍历set
    private void prtSet() {
       if (DEBUG) {
          int index = 0;
          int size = idsSet.size();
          Iterator it = idsSet.iterator();
          Log.d(TAG, "total:"+size);
          while (it.hasNext()) {
             Log.d(TAG, index + " -- " + ((Integer)it.next()).intValue());
          }
       }
    }
}
AppWidgetProvider重要的几个重写方法,比如onDeleted、onEnabled、onDisabled、onReceive、onUpdate方法的作用已经在代码里面做了注释

5. 编辑ExampleAppWidgetService.java

public class ExampleAppWidgetService extends Service {
   
   private static final String TAG="ExampleAppWidgetService"; 

   // 更新 widget 的广播对应的action
   private final String ACTION_UPDATE_ALL = "com.skywang.widget.UPDATE_ALL";
   // 周期性更新 widget 的周期
   private static final int UPDATE_TIME = 5000;
   // 周期性更新 widget 的线程
   private UpdateThread mUpdateThread;
   private Context mContext;
   // 更新周期的计数
   private int count=0;   

   @Override
   public void onCreate() {
      
      // 创建并开启线程UpdateThread
      mUpdateThread = new UpdateThread();
      mUpdateThread.start();
      
      mContext = this.getApplicationContext();

      super.onCreate();
   }
   
   @Override
   public void onDestroy(){
      // 中断线程,即结束线程。
        if (mUpdateThread != null) {
           mUpdateThread.interrupt();
        }
        
      super.onDestroy();
   }
   
   @Override
   public IBinder onBind(Intent intent) {
      return null;
   }

   /*
    * 服务开始时,即调用startService()时,onStartCommand()被执行。
    * onStartCommand() 这里的主要作用:
    * (01) 将 appWidgetIds 添加到队列sAppWidgetIds中
    * (02) 启动线程
    */
   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      Log.d(TAG, "onStartCommand");     
      super.onStartCommand(intent, flags, startId);
      
       return START_STICKY;
   }
   
    private class UpdateThread extends Thread {

        @Override
        public void run() {
            super.run();

            try {
               count = 0;
               while (true) {
                  Log.d(TAG, "run ... count:"+count);
                  count++;

                 Intent updateIntent=new Intent(ACTION_UPDATE_ALL);
                 mContext.sendBroadcast(updateIntent);
                 
                   Thread.sleep(UPDATE_TIME);
               } 
            } catch (InterruptedException e) {
               // 将 InterruptedException 定义在while循环之外,意味着抛出 InterruptedException 异常时,终止线程。
                e.printStackTrace();
            }
        }
    }
}

(01) onCreate() 在创建服务时被执行。它的作用是创建并启动线程UpdateThread()。
(02) onDestroy() 在销毁服务时被执行。它的作用是注销线程UpdateThread()。
(03) 服务UpdateThread 每隔5秒,发送1个广播ACTION_UPDATE_ALL。广播ACTION_UPDATE_ALL在ExampleAppWidgetProvider被处理:用来更新widget中的图片。

6.编译代码生成apk文件,将其安装到手机

7.长按手机的桌面(不同品牌手机可能操作方式不一样)会弹出桌面设置界面,选择添加工具

widget在添加到桌面前的效果图

widget在添加到桌面后的效果图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
创建 Android 应用程序中的 App Widget,需要以下步骤: 1. 创建 App Widget Provider 类 ```java public class MyWidgetProvider extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // 更新 App Widget RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); views.setTextViewText(R.id.tv_widget, "Hello World!"); appWidgetManager.updateAppWidget(appWidgetIds, views); } } ``` 2. 声明 App Widget Provider 在 AndroidManifest.xml 文件中声明 App Widget Provider,如下所示: ```xml <receiver android:name=".MyWidgetProvider" android:label="@string/app_name"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_widget_info" /> </receiver> ``` 其中,MyWidgetProvider 是你的 App Widget Provider 类名,@string/app_name 是你应用的名称,@xml/my_widget_info 是你的 App Widget Provider 的相关信息,需要在 res/xml 目录下创建一个名为 my_widget_info.xml 的文件,并在该文件中指定你的 App Widget 的布局和其他属性。 3. 定义 App Widget 的布局 在 res/layout 目录下创建一个名为 widget_layout.xml 的文件,定义 App Widget 的布局,如下所示: ```xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/widget_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"> <TextView android:id="@+id/tv_widget" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textColor="#000000" /> </RelativeLayout> ``` 4. 更新 App Widget 在 onUpdate() 方法中,更新 App Widget 的 UI。可以使用 RemoteViews 对象更新 App Widget 的 UI,如下所示: ```java @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // 更新 App Widget RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); views.setTextViewText(R.id.tv_widget, "Hello World!"); appWidgetManager.updateAppWidget(appWidgetIds, views); } ``` 5. 安装 App Widget 在应用的首次安装时,需要向用户请求授权,以便创建 App Widget。可以使用以下代码向用户请求授权: ```java // 请求授权 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, new ComponentName(context, MyWidgetProvider.class)); int REQUEST_PICK_APPWIDGET = 1; ((Activity) context).startActivityForResult(intent, REQUEST_PICK_APPWIDGET); ``` 6. 添加 App Widget 到桌面 可以使用以下代码将 App Widget 添加到桌面: ```java // 添加 App Widget Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); context.sendBroadcast(intent); ``` 其中,appWidgetIds 是 App Widget 的 ID 数组,可以在 onUpdate() 方法中获取。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值