在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>

(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>

第二步:创建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>

第三步:创建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();
        }

    }

}

(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;
    }

}

(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);
    }

}

第四步:在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>

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);
            }

        });

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();
            }

        });
    }
}

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

时间: 2024-12-15 03:27:50

在Android 窗口小组件(Widget)中显示(StackView,ListView,GridView)集合View的相关文章

创建 widget 窗口小组件

随着android的手机屏幕越来越大,为了丰富屏幕内容 app widget(窗口小组件)被越来越多的应用所使用.app widget 有什么好处呢?它可以在不启动应用程序的情况下,让用户在屏幕上有一块交互窗口和程序入口点. 这是我手机自带的天气预报app widget效果. 为了创建一个应用程序的widget我们需要创建三个组件: (1)给widget创建一个布局资源. res/layout/nview.xml <?xml version="1.0" encoding=&quo

Android在非UI线程中显示Toast

[java] view plaincopyprint? public void showToast(String msg){ Looper.prepare(); Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show(); Looper.loop(); } public void showToast(String msg){ Looper.prepare(); Toast.makeText(getApplicat

【转】引入android项目在eclipse ADT中显示中文乱码问题

(1)修改工作空间的编码方式:Window->Preferences->General->Workspace->Text file Encoding在Others里选择需要的编码方式,然后保存. (2)修改单个工程的编码方式:右击工程,在弹出的菜单中选择最后一项“Properties”在打开的新窗口左边的菜单树中选择 Info(即第一个),然后在右面找到 Text file encoding ,选择 “other”,在下拉框中选择需要的编码方式. (3)修改一类文件的编码方式:假设

android笔记之在WebView中显示ProgressBar的两种方法

http://blog.csdn.net/liuzhidong123/article/details/6450334

Android获取服务器Json字符串并显示在ListView上面

已经好久没有更新博客,今天终于有新的东西可以记录了. 通过这次的任务学习到了以前没有注意到的知识点,真的有种书读百遍,其义自见的感觉.这次又重新认识了<Handler消息机制原理>.这次的任务中有更新UI.但是忘记了在Android4.0以后不能在UI线程访问网络,子线程也不能更新UI界面.下面我来展示一下这次的效果图. 这次的任务是:获取服务器端的json字符串,并解析显示在Android界面上. 当我接到这个任务的时候,首先想到的是利用Fragment布局加上ListView布局.但是因为

Android开发工程师文集-1 小时学会Widget小组件开发

前言 大家好,给大家带来Android开发工程师文集-1 小时学会Widget小组件开发的概述,希望你们喜欢 学会用Widget (小组件) Widget小组件很方便,很快捷,可以个性化,自己定制,相关功能,可以实时更新最新内容. Widget布局,Widget配置,AppWidgetProvider与Configuration Activity Android Widget xml布局类型:AppWidget Provider <appwidget-provider xmlns:android

097在进度条中显示进度百分比

效果如下: ViewController.h 1 #import <UIKit/UIKit.h> 2 #import "KMProgressViewWithLabel.h" 3 4 @interface ViewController : UIViewController 5 @property (strong, nonatomic) KMProgressViewWithLabel *progressViewCustom; 6 7 @end ViewController.m

为何不能在viewDidLoad方法中显示其他视图

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 你可以使得当视图控制器(view controller)显示自身的时候显示分享选项控制器.你视图控制器的viewDidAppear方法将在它显示在屏幕上的时候被调用,并且在你App中的所有继承体系中的视图都是如此.这意味着你可以用这个机会在你的视图控制器视图上面显示其他视图. 不要试图在你的视图控制器的viewDidLoad方法中显示活动视图控制器(activ

Android 之窗口小部件详解--App Widget

1 App Widget简介 App Widget是应用程序窗口小部件(Widget)是微型的应用程序视图,它可以被嵌入到其它应用程序中(比如桌面)并接收周期性的更新.你可以通过一个App Widget Provider来发布一个Widget. 本文参考Android官方文本,先介绍App Widget的主要组件,然后再以示例来详细说明. 2 App Widget主要的相关类介绍 2.1 AppWidgetProvider AppWidgetProvider 继承自 BroadcastRecei