在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

在Android 窗口小组件(Widget)中显示(StackView,ListView,GridView)集合View,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。