• 在Android 窗口小组件(Widget)中显示(StackView,ListView,GridView)集合View


    在Android 3.0 中引入了 Collection View Widget。用于在窗口小组件中添加了对集合View 的支持。

    如下:

    (1)StackView 一个卡片View,以层叠的方式显示其子View。

    (2)ListView 和传统的ListView一样

    (3)GridView 网格列表。具体用法和传统的一样。

    第一步:创建Widget布局文件   

    (1)Wdiget的布局文件 路径:res/layout/my_widget_layout.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:background="#FF555555"
        android:orientation="vertical" >
    
        <Button
            android:id="@+id/button_refresh"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="刷新" />
    
        <ListView
            android:id="@+id/widget_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:cacheColorHint="#00000000"
            android:scrollbars="none" />
        <!-- 此处的ListView 可以换成StackView或者GridView -->
    
    </LinearLayout>
    View Code

    (2)在集合中显示的项  路径:res/layout/my_widget_layout_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/widget_list_item_layout"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#00000000"
        android:orientation="vertical" >
    
        <TextView
            android:id="@+id/widget_list_item_tv"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="10dip"
            android:layout_marginTop="10dip"
            android:lineSpacingExtra="4dip"
            android:textColor="#FFFFF0"
            android:textSize="20sp" />
    
    </LinearLayout>
    View Code

    第二步:创建Widget的描述文件

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:initialLayout="@layout/my_widget_layout"
        android:minHeight="280dp"
        android:minWidth="280dp"
        android:previewImage="@drawable/ic_launcher"
        android:resizeMode="horizontal|vertical"
        android:updatePeriodMillis="0" >
    
        <!--
            sdk1.5之后updatePeriodMillis已失效,置为0,循环执行自行在代码中实现。
            至于其他属性可以查一下。在其他随笔中我也给出了
        -->
    
    </appwidget-provider>
    View Code

    第三步:创建Class文件

    (1)创建一个MyAppWidgetProvider.class文件,继承自AppWidgetProvider(PS:这个类主要处理Widget中的各种操作。如更新,删除,是否可用等)

    package com.example.mywidget;
    
    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.net.Uri;
    import android.widget.RemoteViews;
    import android.widget.Toast;
    
    public class MyAppWidgetProvider extends AppWidgetProvider {
    
        private String clickAction = "itemClick";
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                int[] appWidgetIds) {
    
            // 获取Widget的组件名
            ComponentName thisWidget = new ComponentName(context,
                    MyAppWidgetProvider.class);
    
            // 创建一个RemoteView
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                    R.layout.my_widget_layout);
    
            // 把这个Widget绑定到RemoteViewsService
            Intent intent = new Intent(context, MyRemoteViewsService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[0]);
    
            // 设置适配器
            remoteViews.setRemoteAdapter(R.id.widget_list, intent);
    
            // 设置当显示的widget_list为空显示的View
            remoteViews.setEmptyView(R.id.widget_list, R.layout.none_data);
    
            // 点击列表触发事件
            Intent clickIntent = new Intent(context, MyAppWidgetProvider.class);
            // 设置Action,方便在onReceive中区别点击事件
            clickIntent.setAction(clickAction);
            clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));
    
            PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(
                    context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            remoteViews.setPendingIntentTemplate(R.id.widget_list,
                    pendingIntentTemplate);
    
            // 刷新按钮
            final Intent refreshIntent = new Intent(context,
                    MyAppWidgetProvider.class);
            refreshIntent.setAction("refresh");
            final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(
                    context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.button_refresh,
                    refreshPendingIntent);
    
            // 更新Wdiget
            appWidgetManager.updateAppWidget(thisWidget, remoteViews);
    
        }
    
        /**
         * 接收Intent
         */
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
    
            String action = intent.getAction();
    
            if (action.equals("refresh")) {
                // 刷新Widget
                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                final ComponentName cn = new ComponentName(context,
                        MyAppWidgetProvider.class);
    
                MyRemoteViewsFactory.mList.add("five");
                // 这句话会调用RemoteViewSerivce中RemoteViewsFactory的onDataSetChanged()方法。
                mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn),
                        R.id.widget_list);
    
            } else if (action.equals(clickAction)) {
                // 单击Wdiget中ListView的某一项会显示一个Toast提示。
                Toast.makeText(context, intent.getStringExtra("content"),
                        Toast.LENGTH_SHORT).show();
            }
    
        }
    
    }
    View Code

    (2)创建一个MyRemoteViewsFactory.class文件,该类实现了RemoteViewsFactory接口

    package com.example.mywidget;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.content.Context;
    import android.content.Intent;
    import android.widget.RemoteViews;
    import android.widget.RemoteViewsService.RemoteViewsFactory;
    
    public class MyRemoteViewsFactory implements RemoteViewsFactory {
    
        private final Context mContext;
        public static List<String> mList = new ArrayList<String>();
    
        /*
         * 构造函数
         */
        public MyRemoteViewsFactory(Context context, Intent intent) {
    
            mContext = context;
        }
    
        /*
         * MyRemoteViewsFactory调用时执行,这个方法执行时间超过20秒回报错。
         * 如果耗时长的任务应该在onDataSetChanged或者getViewAt中处理
         */
        @Override
        public void onCreate() {
            // 需要显示的数据
            mList.add("one");
            mList.add("two");
            mList.add("three");
            mList.add("four");
        }
    
        /*
         * 当调用notifyAppWidgetViewDataChanged方法时,触发这个方法
         * 例如:MyRemoteViewsFactory.notifyAppWidgetViewDataChanged();
         */
        @Override
        public void onDataSetChanged() {
        }
    
        /*
         * 这个方法不用多说了把,这里写清理资源,释放内存的操作
         */
        @Override
        public void onDestroy() {
            mList.clear();
        }
    
        /*
         * 返回集合数量
         */
        @Override
        public int getCount() {
            return mList.size();
        }
    
        /*
         * 创建并且填充,在指定索引位置显示的View,这个和BaseAdapter的getView类似
         */
        @Override
        public RemoteViews getViewAt(int position) {
            if (position < 0 || position >= mList.size())
                return null;
            String content = mList.get(position);
            // 创建在当前索引位置要显示的View
            final RemoteViews rv = new RemoteViews(mContext.getPackageName(),
                    R.layout.my_widget_layout_item);
    
            // 设置要显示的内容
            rv.setTextViewText(R.id.widget_list_item_tv, content);
    
            // 填充Intent,填充在AppWdigetProvider中创建的PendingIntent
            Intent intent = new Intent();
            // 传入点击行的数据
            intent.putExtra("content", content);
            rv.setOnClickFillInIntent(R.id.widget_list_item_tv, intent);
    
            return rv;
        }
    
        /*
         * 显示一个"加载"View。返回null的时候将使用默认的View
         */
        @Override
        public RemoteViews getLoadingView() {
            return null;
        }
    
        /*
         * 不同View定义的数量。默认为1(本人一直在使用默认值)
         */
        @Override
        public int getViewTypeCount() {
            return 1;
        }
    
        /*
         * 返回当前索引的。
         */
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        /*
         * 如果每个项提供的ID是稳定的,即她们不会在运行时改变,就返回true(没用过。。。)
         */
        @Override
        public boolean hasStableIds() {
            return true;
        }
    
    }
    View Code

    (3)创建MyRemoteViewsService.class 文件,该类继承了RemoteViewsService

    package com.example.mywidget;
    
    import android.content.Intent;
    import android.widget.RemoteViewsService;
    
    public class MyRemoteViewsService extends RemoteViewsService {
    
        @Override
        public RemoteViewsFactory onGetViewFactory(Intent intent) {
            return new MyRemoteViewsFactory(this.getApplicationContext(), intent);
        }
    
    }
    View Code

    第四步:在AndroidManifest.xml 文件中注册Widget以及相关类文件

    <!-- Widget必须添加到manifest文件中,和Broadcaset Receiver一样使用“receiver”标签 -->
            <receiver android:name=".MyAppWidgetProvider" >
                <!-- 此处设置Wdiget更新动作 -->
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
                <!-- 此处设置Widget的描述资源res/xml/my_widget.xml -->
                <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/my_widget" >
                </meta-data>
            </receiver>
    
            <!-- 注册Service,注意:一定要加权限:android.permission.BIND_REMOTEVIEWS -->
            <service
                android:name=".MyRemoteViewsService"
                android:exported="false"
                android:permission="android.permission.BIND_REMOTEVIEWS" >
            </service>
    View Code

    PS:如何在应用程序中控制Widget

    (1)在应用程序中通知Wdiget刷新。

    //通过广播方式,通知Widget刷新
    //这个Action是在Manifext.xml 中注册的,主意这个Action不要和系统的Action重复咯
    Intent intent = new Intent("android.appwidget.action.APPWIDGET_UPDATE");
    sendBroadcast(intent);

    在MyAppWidgetProvider中的onReceive中处理这个Intent。(ps:和刷新按钮处理方式一样。对比然后执行相关的方法)

    (2)在Activity中更新Widget界面

        Button btn = (Button) findViewById(R.id.button1);
            btn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    
                    // 获取Widget管理器
                    AppWidgetManager awm = AppWidgetManager
                            .getInstance(MainActivity.this);
    
                    // Widget组件名字
                    ComponentName compe = new ComponentName(MainActivity.this,
                            MyAppWidgetProvider.class);
    
                    // 创建RemoteViews
                    RemoteViews rv = new RemoteViews(MainActivity.this
                            .getPackageName(), R.layout.my_widget_layout);
                    // 修改RemoteViews中的刷新按钮
                    // 可以只用RemoteViews.setXXX对应的方法修改RemoteView中的组件。
                    rv.setTextViewText(R.id.button_refresh, "刷新Refresh");
                    
                    // 设置字体颜色
                    rv.setTextColor(R.id.button_refresh, Color.RED);
    
                    //更新Widget
                    awm.updateAppWidget(compe, rv);
                }
    
            });
    View Code

    PS:如何在创建Widget时,启动Acitivity对其设置。


    有时候我们可能面临,在把窗口组件拖动到主屏幕的时候。需要启动Activity对其进行相应的设置。如系统自带的Mail。实现步骤如下:

    (1)在Widget描述文件中添加如下属性:(我的描述文件路径:res/xml/my_widget.xml)

    android:configure="com.example.mywidget.MainActivity"

    主意:一定要使用完全限定名(包名+Acitivity名)

    (2)为了省事我就在MainActivity.class写了。

    package com.example.mywidget;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.appwidget.AppWidgetManager;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.graphics.Color;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.RemoteViews;
    
    public class MainActivity extends Activity {
    
        private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 启动配置Activity的Intent会包含一个EXTRA_APPWIDGET_ID的Extra,该Extra参数就是需要配置的Widget的ID
            Intent intent = this.getIntent();
            Bundle bundle = intent.getExtras();
            if (null != bundle) {
                // 获取Widget的ID
                appWidgetId = bundle.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
                        AppWidgetManager.INVALID_APPWIDGET_ID);
            }
    
            // 如果用户不接受配置就退出Acitivity。那么把结果设为canceld
            setResult(RESULT_CANCELED);
    
            Button btn = (Button) findViewById(R.id.button1);
            btn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
    
                    // 获取Widget管理器
                    AppWidgetManager awm = AppWidgetManager
                            .getInstance(MainActivity.this);
    
                    // Widget组件名字
                    ComponentName compe = new ComponentName(MainActivity.this,
                            MyAppWidgetProvider.class);
    
                    // 创建RemoteViews
                    RemoteViews rv = new RemoteViews(MainActivity.this
                            .getPackageName(), R.layout.my_widget_layout);
                    // 修改RemoteViews中的刷新按钮
                    // 可以只用RemoteViews.setXXX对应的方法修改RemoteView中的组件。
                    rv.setTextViewText(R.id.button_refresh, "刷新Refresh");
    
                    // 设置字体颜色
                    rv.setTextColor(R.id.button_refresh, Color.RED);
    
                    // 更新Widget
                    awm.updateAppWidget(compe, rv);
    
                    // 通知Widget Manager配置已经完成
                    Intent result = new Intent();
                    result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                            appWidgetId);
                    // 注意:此时会执行MyAppWidgetProvider中的onReceive方法,其他方法包括onUpdate不会执行。绑定数据最好在onReceive中执行
                    setResult(RESULT_OK, result);
    
                    // 关闭设置页
                    finish();
                }
    
            });
        }
    }
    View Code
  • 相关阅读:
    【Java】Caused by: com.ibatis.sqlmap.client.SqlMapException: There is no statement named *** in this SqlMap.
    【Mac】Mac 使用 zsh 后, mvn 命令无效
    【Java】Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: 1099
    【Android】drawable VS mipmap
    【Android】java.lang.SecurityException: getDeviceId: Neither user 10065 nor current process has android.permission.READ_PHONE_STATE
    java sql解析
    java动态编译
    随想
    一致hash算法
    一致性哈希算法及其在分布式系统中的应用
  • 原文地址:https://www.cnblogs.com/ywtk/p/3858326.html
Copyright © 2020-2023  润新知