转载请标明出处:http://blog.csdn.net/sk719887916/article/details/47027263

不少开发项目中都会有widget功能,别小瞧了它,他也是android的七大组件之一,对widget陌生的朋友可以阅读下我的上篇文章< Android Widget工作原理详解(一)>关于内部的介绍,还没掌握的同学不要担心,开发AppWidget套路很简单,今天我们就实现一个可以加入listView滑动的widget,熟悉下一个普通widget的开发步骤。

一 创建AppWidgetProvider

此类是widget的控制核心,主要控制添加,删除,更新等。他是Broadcast的子类,可以拥有广播的一切特性。
创建 MyAppListWidgetProvider类继承AppWidgetProvider,实现其一下方法,onUpdate(),onReceive(), onEnabled(Context context) , onDeleted(), onDisabled()后面三方法可选而不可选。

1 onUpdate()
此方法一般处理widget的创建布局和更新UI操作,当widget添加到桌面会触发onUpdate()方法,接下我们可以在此里通过获取remoteViews来给widget加载一个布局,远程视图前面也说过,它是widget的资源管理工具,我们可以用来给widget转换一个它支持布局,仅支持特定的view,下面我为它加载一个listView,在widget上给某个控件设置点击事件采用PendingIntent,通过new一个延时意图,然后remoteViews.setOnClickPendingIntent()来注册点击事件。更新布局可以获得用WidgetManager..updateAppWidget(thisWidget,
remoteViews)来加载或更新widget布局,也可以通过onReceive()
收到一个自定义的广播来调用此方法更新布局也可以。
 @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {
        // 获取Widget的组件名
        ComponentName thisWidget = new ComponentName(context,
                MyAppListWidgetProvider.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, MyAppListWidgetProvider.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,
                MyAppListWidgetProvider.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);

    }
     2  onReceive()

 
      此方功类似广播的onReceive()用发,用开接收和处理广播,如果我们在manifest.xml注册了MyAppListWidgetProvider为一个appwidget,那么不必须为此广播加上widget标示,添加一action:<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />,下面的 <meta-data>标签用来定义widget的属性,指定一个widget描述信息,具体释义请阅读 上篇widget原理详解文章,

 <!-- Widget必须添加到manifest文件中,和Broadcaset Receiver一样使用“receiver”标签 -->
        <receiver android:name=".MyAppListWidgetProvider" >
            <!-- 此处设置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/widget_info"
               >
            </meta-data>
        </receiver>

onReceive里处理代码逻辑,比如我这里用来接收widget的用来更新我们在onUpdate()给刷新按钮定义的点击事件,处理刷新界面需求,

 
/**
     * 接收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,
                    MyAppListWidgetProvider.class);

            MyRemoteViewsFactory.mList.add("音乐"+i);

            // 这句话会调用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();
        }
        i=i+1;
    }

    3 onEnabled(Context context) 

    当wdiget可用正在拖进桌面时触发,这里我们一般可以进行一些变量初始的工作
   
 @Override
    public void onEnabled(Context context) {
    	// TODO Auto-generated method stub
    	super.onEnabled(context);

        Toast.makeText(context, "用户将widget添加桌面了",
                Toast.LENGTH_SHORT).show();
    }

    

 4 onDeleted(), 
   widget被删除了,这里我们通常用于释放一些对象和视图资源,便于防止内存泄露。

 
   @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
    	// TODO Auto-generated method stub。
        Toast.makeText(context, "用户将widget从桌面移除了",
                Toast.LENGTH_SHORT).show();
    	super.onDeleted(context, appWidgetIds);
    }


5  onDisabled()
   widget在被拖动的时候触发,这是widget是无法点击的,当停止拖动操作时widget可用。

二 创建RemoteViewsFactory

   远程视图工厂,用来返回Remoteviews,其通过adpter进行工作的,这里我们调用  MyRemoteViewsFactory.mList.add()方法;给widget上的Listview加入数据。

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("");
	        for (int i = 0; i < 5; i++) {
	        	  mList.add("item"+ i);
			}

	    }

	    /*
	     * 当调用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;
	    }

三  RemoteViewsService

RemoteViewsService子类提供了RemoteViewsFactory用于填充远程集合视图。

具体地说,其子类RemoteViewsService是一个远程的服务适配器 可以请求RemoteViews,管理RemoteViews的服务。我们继承RemoteViewsService来获得一个视图工厂,

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class MyRemoteViewsService extends RemoteViewsService {

	@Override
	public RemoteViewsFactory onGetViewFactory(Intent intent) {

		   return new MyRemoteViewsFactory(this.getApplicationContext(), intent);
	}

}


四 增加widet基础属性配置

  1  添加widget描述文件
     
    我们为widget新增一个描述xml,在res/下新建一个xml文件目录,然后新建widget_info.xml文件,具体如下
   给widget指定了最小的宽高和浏览的基础视图,包括其具体的布局文件。具体介绍请看上篇文章 ---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="120dp"
    android:minWidth="280dp"
    android:previewImage="@drawable/ic_launcher"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="0" >

    <!--
        sdk1.5之后updatePeriodMillis已失效,置为0,循环执行自行在代码中实现。
        至于其他属性可以查一下。在其他随笔中我也给出了
    -->

</appwidget-provider>

 

2  新建widget资源文件xml

    
    为widget新建一个实际要加载,也就是我直观的看到的视图布局。此布局通过widgetInfo的android:initialLayout="@layout/my_widget_layout"

属性来指定。而widget描述信息我们在manifest.xml中 用<meta-data>标签用来指定。

<?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="200dp"
    android:background="@android:color/white"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button_refresh"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
         android:background="@drawable/lite_widget_item_choosed_background_icon"
        android:textColor="@android:color/white"
        android:layout_marginTop="2dp"
        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>

    通过以上的步骤我们简单的实现了一个widget,用于初学者学习和交流,比较复杂的widget逻辑我们还会加入网络访问功能,和一些和sevice进行数据交互,如果想要widget实现自动加入到桌面,或者widget支持自定义控件的话,第一可以将我的app变成系统app,第二,采用重写Remoteviews来支持我们自定义的view,具体实现逻辑后面再介绍,谢谢阅读。

  
  

Android Widget 开发详解(二) +支持listView滑动的widget的更多相关文章

  1. Android WebView 开发详解(二)

    转载请注明出处  http://blog.csdn.net/typename/article/details/39495409 powered by miechal zhao   概览: Androi ...

  2. Android USB 开发详解

    Android USB 开发详解 先附上 Android USB 官方文档 Android通过两种模式支持各种 USB 外设和 Android USB 附件(实现Android附件协议的硬件):USB ...

  3. JMessage Android 端开发详解

    目前越来越多的应用会需要集成即时通讯功能,这里就为大家详细讲一下如何通过集成 JMessage 来为你的 App 增加即时通讯功能. 首先,一个最基础的 IM 应用会需要有哪些功能? 用户注册 / 登 ...

  4. Android WebView 开发详解

    Android WebView 开发详解 参见 http://blog.csdn.net/typename/article/details/39030091

  5. 《Android游戏开发详解》一1.7 控制流程第1部分——if和else语句

    本节书摘来异步社区<Android游戏开发详解>一书中的第1章,第1.7节,译者: 李强 责编: 陈冀康,更多章节内容可以访问云栖社区"异步社区"公众号查看. 1.7 ...

  6. Android Studio 插件开发详解二:工具类

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78112856 本文出自[赵彦军的博客] 在插件开发过程中,我们按照开发一个正式的项 ...

  7. Android 时间日期Widget 开发详解

    桌面Widget其实就是一个显示一些信息的工具(现在也有人开发了一些有实际操作功能的widget.例如相机widget,可以直接桌面拍照).不过总的来说,widget主要功能就是显示一些信息.我们今天 ...

  8. Android WebView 开发详解(一)

    转载请注明出处  http://blog.csdn.net/typename/article/details/39030091 powered by meichal zhao 概览: Android ...

  9. Android WebView 开发详解(三)

    转载请注明出处   http://blog.csdn.net/typename/article/details/40302351 powered by miechal zhao 概览 Android ...

随机推荐

  1. jQuery中$(function()与(function($)等的区别详细讲解

    (function($) {-})(jQuery); 这里实际上是匿名函数,如下: function(arg){-} 这就定义了一个匿名函数,参数为arg 而调用函数时,是在函数后面写上括号和实参的, ...

  2. Android视频媒体相关,VideoView和开源框架vitamio

    虽然Android已经内置了VideoView组件和MediaPlayer类来支持开发视频播放器,但支持格式.性能等各方面都十分有限,但是Vitamio的确强大到没朋友! Vitamio 是一款 An ...

  3. RxJava操作符(04-过滤操作)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51656494 本文出自:[openXu的博客] 目录: Debounce Distinct ...

  4. 剑指Offer——完美+今日头条笔试题+知识点总结

    剑指Offer--完美+今日头条笔试题+知识点总结 情景回顾 时间:2016.9.28 16:00-18:00 19:00-21:00 地点:山东省网络环境智能计算技术重点实验室 事件:完美世界笔试 ...

  5. 1git命令的使用,查看git仓库状态,添加文件到git跟踪,git提交,查看git分支,查看git仓库日志信息,切换git分支,解决git分支合并后出现冲突的问题

    1新建一个存储git的文件夹,命令是: toto@toto-K45VD:~$ mkdir gitfolder 2初始化一个git仓库,命令是: toto@toto-K45VD:~$cd gitfold ...

  6. ROS_Kinetic_26 使用rosserial_windows实现windows与ROS master发送与接收消息

    使用rosserial_windows实现windows与ROS master发送与接收消息(适用版本hydro,indigo,jade,kinetic) 官方wiki地址汇总请参考:http://b ...

  7. Java HashMap并发死循环

    在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环.这个事情我4. ...

  8. Spring入门介绍-IOC(二)

    浅谈IOC IOC(inversion of control)是Spring的核心,贯穿始终.所谓IOC 就是有Spring来控制对象的生命周期和对象间的关系. 传统开发模式:对象之间相互依赖 IOC ...

  9. SQL 数据库语言分析总结(二)

    介绍sql语言 我们接着一的顺序继续介绍这个语言 数据类型 整形: TINYINT(8位) SMALLINT(16位) MEDIUMINT(24位) INT(32位) BIGINT(64位) 实数: ...

  10. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...