由于工作需求,上周刚开始接触widget。经过一周的开发和了解,对widget的一些基础点做一下整理。
什么是widget:
widget也叫小部件,在launcher中点击menu键,会出现小部件选项,进入后会显示所以手机上已经安装的widget。长按或者单击(launcher决定)后,可添加到手机桌面上。
从功能上,widget功能比较简单,专注于某一件事情。比如时钟、天气、音乐等等。
widget也可以和响应的普通应用联系起来,点击之后进入应用界面。这时候widget更像是一个应用的快捷入口。
下面来具体介绍一下widget的开发步骤:
在manifest中,注册一个receiver,用来接收系统发来的widget的广播。
<!-- receiver的 android:name指向的是widget的请求处理器或者说请求接收者 --> <receiver android:label="@string/app_name" android:name="com.cooee.phenix.widget.WidgetPRovider"> <intent-filter> <!-- 点击事件对应的广播字符串 --> <action android:name="com.cooee.phenix.widget.click.clock_layout"/> <action android:name="com.cooee.phenix.widget.click.weather_layout"/> <action android:name="com.cooee.phenix.widget.click.date_layout"/> <!-- widget默认的事件action --> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <!-- widget元数据,name是写死的,resource指的是widget的配置文件 --> <meta-data android:name="android.appwidget.provider" android:resource="@layout/widgetinfo" /> </receiver>2、WidgetProvider处理器
WidgetProvider是我们自己创建的类,继承AppWidgetProvider,AppWidgetProvider extends BroadcastReceiver,用来处理widget的各种请求。
widget中的事件都是通过广播进行传递的:
widget的添加、删除、更新等操作,都会收到相应的广播。在AppWidgetProvider的public void onReceive(方法中可以看到
AppWidgetProvider类中:
public void onUpdate:widget更新时回调
public void onDeleted:一个widget从桌面删除时回调
public void onEnabled:第一个widget被放到桌面时回调
public void onDisabled:最后一个widget从桌面删除时回调
public void onReceive:用来接收广播,处理widget的消息。
如果我们需要在这些时刻做一下自己的逻辑处理,WidgetProvider可以重写这几个方法。
3、widgetinfo和widget布局
widget的界面和widget宽高等信息在哪里定义呢?
在上面代码的注释中可以看到,widgetinfo是关联在receiver里的mete-data中。(把这部分代码单独贴出来方便大家看)<!-- widget元数据,name是写死的,resource指的是widget的配置文件 --> <meta-data android:name="android.appwidget.provider" android:resource="@layout/widgetinfo" />那么,widgetinfo中究竟定义了什么东西,我们来具体看一下这个xml文件:<?xml version="1.0" encoding="utf-8"?><!-- appwidget-provider Widget的配置文件 --><!-- android:minWidth 最小宽度 --><!-- android:minHeight 最小高度 --><!-- android:updatePeriodMillis 组件更新频率(毫秒) --><!-- android:initialLayout 用来关联widget的布局文件 --><!-- android:configure Widget设置用Activity ??? --><appwidget-provider android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android" android:initialLayout="@layout/widget_layout" android:minWidth="@dimen/widget_width" android:minHeight="@dimen/widget_height" android:updatePeriodMillis="86400000" />关于android:configure属性,这次并没有用到,具体我也不懂这是干嘛用的。在这里,我最关心的是布局文件,毕竟,它是要显示的东西。由于布局文件比较长这里就不贴代码了。(布局文件就像我们app里的一样布局就ok啦)
4、widget点击事件(action的意义)
说道点击事件,我们得先来说一下widget中的view。
remoteview:
上面讲到widget关联布局的方式,和我们写app有点不同。那么 我们想对显示的布局做一些动态的改变(改变文字或者背景之类),该怎么办呢?
按照写app的思考方式,我们需要获取到一个view,它里面填充的整个布局,然后最好有个findviewbyid获取到布局里每一个控件,然后,嘎嘎嘎,我们就能为所欲为了。
设置点击监听、改变文字、设置图片背景神马的还不是洒洒水???!!!
想法,是很美好的。但是百度了一番之后我才发现并没有想象中辣么简单。。。。。。
下面来介绍widget如何获取我们想要的view:
rv = new RemoteViews( mContext.getPackageName() , R.layout.widget_layout );得到view之后,悲剧的事情发生了,它居然没有findviewbyid方法!!!!!也就是说我得不到布局里每一个具体的view一番网上搜索之后,设置点击事件监听和改变文字之类的找到了对应的方法,不过,动画好像就与我们绝缘了。。。
点击事件:
//日期Intent intentDateClick = new Intent( WidgetProvider.CLICK_DATE_LAYOUT );//intent的action用来区分具体点击的是哪一个viewPendingIntent pendingDateIntent = PendingIntent.getBroadcast( mContext , 0 , intentDateClick , 0 );rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );//设置点击事件监听//时钟if( showClockVeiw ){ Intent intentClockClick = new Intent( WidgetProvider.CLICK_CLOCK_LAYOUT ); PendingIntent pendingClockIntent = PendingIntent.getBroadcast( mContext , 0 , intentClockClick , 0 ); rv.setOnClickPendingIntent( R.id.clock_layout , pendingClockIntent );}visibility = showClockVeiw ? View.VISIBLE : View.GONE;rv.setViewVisibility( R.id.clock_layout , visibility );//天气if( showWeatherVeiw ){ Intent intentWeatherClick = new Intent( WidgetProvider.CLICK_WEATHER_LAYOUT ); PendingIntent pendingWeatherIntent = PendingIntent.getBroadcast( mContext , 0 , intentWeatherClick , 0 ); rv.setOnClickPendingIntent( R.id.city_textview , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.weather_city , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.temperature_current , pendingWeatherIntent ); rv.setOnClickPendingIntent( R.id.temperature_range , pendingWeatherIntent );}visibility = showWeatherVeiw ? View.VISIBLE : View.GONE;setWeatherVisibility( visibility );上面代码可以看到。我们的关键点在于:rv.setOnClickPendingIntent( R.id.date_textview , pendingDateIntent );就是这个方法设置了点击事件的监听。第一个参数是rv这个view中,布局里面的某一个view的id,第二个参数是PendingIntent是具体的广播意图。里面包含了如果点击这个view,WidgetProvider将受到的广播。
widget中,点击事件是通过广播传递的。intent的action就是我们在manifest里看到的三个action。所以我们用三个action来区分三种view的点击事件。
下面贴出来三个action的变量值,方便大家逻辑跟踪。这三个值都是WidgetProvider中的:
public static final String CLICK_CLOCK_LAYOUT = "com.cooee.phenix.widget.click.clock_layout";public static final String CLICK_WEATHER_LAYOUT = "com.cooee.phenix.widget.click.weather_layout";public static final String CLICK_DATE_LAYOUT = "com.cooee.phenix.widget.click.date_layout";通过点击事件的启发,我发现虽然我们不能具体拿到布局里的每一个view,但是我们可以通过remoteview来改变它。
比如这句:rv.setViewVisibility( R.id.clock_layout , visibility );//设置view是否可见,我似乎找到了某种规律。至此,我们已经设置好了widget中view的点击事件。
当我们点击view时,WidgetProvider就会收到广播,我们通过区分intent的action来处理不同view的点击:
public void onClick( String action ){ //点击时钟布局 if( action.equals( WidgetProvider.CLICK_CLOCK_LAYOUT ) ) { ClockCalendarManager.getInstance( mContext ).onClickClock(); } //点击日期 else if( action.equals( WidgetProvider.CLICK_DATE_LAYOUT ) ) { ClockCalendarManager.getInstance( mContext ).onclickDate(); } //点击天气 else if( action.equals( WidgetProvider.CLICK_WEATHER_LAYOUT ) ) { WeatherManager.getInstance( mContext ).onClick(); } else { Log.d( TAG , "其他" ); }}5、widget修改后更新显示
以上我们讲了widget的manifest注册、widgetinfo、widget布局、如何获取view、如何设置点击事件、如何响应点击事件。
下面我们来看看如何更新一个view显示的内容、如何更新我们的widget。
以我们的时钟为例,当我们需要更新view时:
TwinkleClockwidgetManager instance = TwinkleClockwidgetManager.getInstance( mContext );RemoteViews rv = instance.getRv();if( instance.showClockVeiw ){ rv.setImageViewResource( R.id.clock_minute_tens , timeNumbers[mCurrentMinute / 10] ); rv.setImageViewResource( R.id.clock_minute_ones , timeNumbers[mCurrentMinute % 10] );}哈哈,果然跟想的一样,都是通过remoteview来设置,第一个参数是要改变的view的id,第二个是要设置的值。改变完view之后,我们需要更新一下widget,就能改变桌面上widget的显示了:
//更新插件AppWidgetManager appWidgetManger = AppWidgetManager.getInstance( mContext );//第一步,通过context拿到appwidgetManagerint[] appIds = appWidgetManger.getAppWidgetIds( new ComponentName( mContext , WidgetProvider.class ) );//第二步,通过widgetManager和包类名拿到widgetIdappWidgetManger.updateAppWidget( appIds , rv );//第三步,更新widget的显示总结:
以上就是这一周多时间对widget的了解,贴一张widget的图展示小成果:嘿嘿嘿
其中还有些坑无法解惑:
1、widget如何做动画?(好像在哪看到过别的桌面的widget,日历时钟,过一天,日历更新时是带一个旋转的动画的)
2、widget如何可拉伸?(launcher上显示widget时,有的widget长按松手之后可以改变widget在桌面上显示的大小,目前还不确定这部分是桌面做?还是需要widget这边协助。。。)
以上仅代表个人观点,如有理解不到位的地方,还请指正。欢迎留言,欢迎私信。
新闻热点
疑难解答