每天一练 2015/04/06 widget的用法

来源:互联网 发布:淘宝账号怎么找回 编辑:程序博客网 时间:2024/05/20 00:09

1.      声明一个widget的布局文件(.xml格式)置于res/layout文件夹下

2.      声明一个widget的元数据文件(.xml格式)置于res/xml文件夹下。在这个元数据文件中至少要声明如下几个属性:minHeight,minWidth,updatePeriodMillis和initialLayout。其中initalLayout的属性值就是第一步中建立的widget布局文件

3.      在AndroidManifest文件中“注册”widget。注册的时候使用的是<receiver>标签。为了体现与一般的receiver的区别,在<receiver>中必须有如下子节点:

<receiver android:name=”.自己的AppWidgetProvider实现类”> 

<intent-filter>

           <action android:name=”android.appwidget.action.APPWIDGET_UPDATE”>

</intent-filter>

<meta-dataandroid:name=”android.appwidget.provider”

               android:resource=”@xml/第2步中声明的widget元数据文件”

/>

</receiver>

4.      声明一个自己的AppWidgetProvider实现类。widget的新建,删除等动作都会触发相应的widget广播,这些广播需要通过继承AppWidgetProvider来接收。通过重写AppWidgetProvider的相应回调方法(onUpdate,onDelete,onEnable等),来处理事件发生时的行为。

注意:1. 因为AppWidgetProvider继承自BroadcastReceiver,那么在写相应的回调方法时必须要考虑ANR问题,不能超时。

            2. 所有widget的UI操作相关操作(例如UI的更新,数据的输入等等)都是不能直接在widget本身上面进行的,必须借助于PendingIntent、RemoteViews和AppWidgetManager来辅助完成

            3. 一个AppWidgetProvider可以对应多个widget。因为一个widget可以设定不同的大小,可以被反复添加到页面上,这样一个AppWidgetProvider就可能要在onUpdate等回调方法中去同时更新多个widget了

一个完整的例子:

本例提供一个widget,widget中有一个刷新按钮,每次点击时,都可以生成一个随机数字。点击widget的文本区域,可以打开一个activity,通过activity中的按钮也可以对widget中的内容进行刷新。

本例需要如下文件:

1)res/layout/simple_widget_layout.xml widget的布局文件

2) res/xml/simple_widet.xml widget的元数据文件

3)AndroidManifest.xml 注册相关activity,receiver,service

4)MyAppWidgetProvider.java 继承自AppWidgetProvider,监听widget的各种动作

5)RandomService.java 继承自Service,完成widget的刷新,以及与MainActivity的通信

6)MainActivity.java 继承自Activiy,提供一个与widget“绑定”组件,通过该Activity利用RandomService,也能进行widget的更新


1)res/layout/simple_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal"    android:padding="10dp"    android:background="@drawable/widget_background"     >    <LinearLayout        android:id="@+id/container"        android:layout_width="0dp"        android:layout_height="wrap_content"        android:layout_gravity="center_vertical"        android:layout_weight="1"        android:orientation="vertical" >        <TextView            android:id="@+id/tv_title"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:text="Random Number"            android:textAppearance="?android:attr/textAppearanceMedium" />        <TextView            android:id="@+id/tv_number"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:textStyle="bold"            android:textAppearance="?android:attr/textAppearanceMedium" />    </LinearLayout>    <ImageButton         android:id="@+id/btn_refresh"        android:layout_width="55dp"        android:layout_height="55dp"        android:layout_gravity="center_vertical"        android:background="@null"        android:contentDescription="@null"        android:src="@android:drawable/ic_menu_rotate"        /></LinearLayout>

widget布局中有两个文本框,tv_title显示提示信息,tv_number显示随机数字,btn_refresh是一个刷新按钮,每次点击,都会生成一个随机数字显示在tv_number中。

2)res/xml/simple_widget.xml

<appwidget-provider     xmlns:android="http://schemas.android.com/apk/res/android"    android:minWidth="180dp"    android:minHeight="40dp"    android:updatePeriodMillis="86400000"    android:initialLayout="@layout/simple_widget_layout"       />
updatePeriodMillis的属性值官方建议不易短于30分钟。如果需求是频繁刷新widget,不要依赖于widget自身的这个时间刷新设定,而应该使用AlarmManager或者Intent等来完成刷新。
3)AndroidManifest.xml (局部)

<activity            android:name="com.example.widgettest.MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                <receiver android:name=".MyAppWidgetProvider">                        <intent-filter >                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>            </intent-filter>            <meta-data                 android:name="android.appwidget.provider"                android:resource="@xml/simple_widget"                />                    </receiver>        <service android:name="RandomService"></service>
一个activity,一个receiver(AppWidgetProvider是BroadcastReceiver的继承者),一个service

4)MyAppWidgetProvider

public class MyAppWidgetProvider extends AppWidgetProvider{@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {Intent intent=new Intent(context,RandomService.class);context.startService(intent);}}
这里只监听onUpdate事件,该事件在widget被新建的时候会被回调。这里不做任何直接处理而是启动一个RandomService来进行数据的生成和widget的刷新

5)RandomService

public class RandomService extends Service{public static final String ACTION="com.example.widgettest.ACTION";public static int sRandomNumber;public static int getRandomNumber(){return sRandomNumber;}@Overridepublic void onCreate() {super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {sRandomNumber=(int) (Math.random()*100);RemoteViews rv=new RemoteViews(getPackageName(), R.layout.simple_widget_layout);rv.setTextViewText(R.id.tv_number, String.valueOf(sRandomNumber));PendingIntent pi1=PendingIntent.getService(this, 0, new Intent(this, RandomService.class), 0);rv.setOnClickPendingIntent(R.id.btn_refresh, pi1);PendingIntent pi2=PendingIntent.getActivity(this, 0, new Intent(this,MainActivity.class), 0);rv.setOnClickPendingIntent(R.id.container, pi2);AppWidgetManager manager=AppWidgetManager.getInstance(this);ComponentName widget=new ComponentName(this, MyAppWidgetProvider.class);manager.updateAppWidget(widget, rv);Intent broad=new Intent(ACTION);sendBroadcast(broad);return START_NOT_STICKY;}@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}}
这里onStartCommand一共做了如下几件事情:

i. 生成一个随机数

ii. 将widget包装成一个RemoteViews,利用RemoteViews提供的API设置组件应该显示的内容。这里的API设计非常讲究,签名是SetTextViewText而不是setTextView,意在明确RemoteViews是不能直接设置相关组件的内容的,只能提供相关相关组件应该显示的内容是什么

iii. 继续利用RemoteViews提供的API,使用PendingIntent为widget里面的相关组件绑定“监听器”

iv.  获得AppWidgetManager,利用AppWidgetManager来做widget的具体更新工作

v.  AppWidgetManager更新完毕后,Service发送一个广播(这个广播的主要作用是在MainActivity中更新widget时会用到的)

通过pi1,为widget的刷新按钮绑定了一个单击响应。每次在widget中单击刷新按钮,RandomService都会被启动,并生成一个随机数,然后通过RemoteViews、AppWidgetManager将结果刷新到widget。pi2为widget单击面板绑定了一个单击响应,当单击widget面板的时候会启动MainActivity。

非常重要的是,任何针对RemoteView的改动(比如显示内容的变化)或者设置(比如为按钮添加单击事件)必须调用AppWidgetManager的 updateAppwidget方法才会让这些改动或者设置生效。

6)MainActivity

public class MainActivity extends Activity {@ViewInject(R.id.tv_main_number)private TextView tv;@ViewInject(R.id.btn_main_generate)private Button btn;private BroadcastReceiver mReceiver=new BroadcastReceiver(){@Overridepublic void onReceive(Context context, Intent intent) {                    updateView();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ViewUtils.inject(this);updateView();}protected void updateView() {tv.setText(String.valueOf(RandomService.getRandomNumber()));}@OnClick({R.id.btn_main_generate})public void doClick(View view){startService(new Intent(this,RandomService.class));}@Overrideprotected void onResume() {super.onResume();IntentFilter filter=new IntentFilter(RandomService.ACTION);registerReceiver(mReceiver, filter);}@Overrideprotected void onPause() {unregisterReceiver(mReceiver);super.onPause();}}
R.layout.activity_main的内容如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <Button        android:id="@+id/btn_main_generate"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Generate Number" >    </Button>    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:text="Current Random Number"        android:textAppearance="?android:attr/textAppearanceLarge" />    <TextView        android:id="@+id/tv_main_number"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:textSize="55sp"        android:textStyle="bold" /></LinearLayout>
当MainActivity新建的时候,tv_main_number会从RandomService中拿数据显示出当前widget显示的数字。当点击btn_main_generate的时候,会启动RandomService,此时如果MainActivity保持在前台,那么它会收到RandomService更新完widget界面后发送的广播。在onReceive方法中,会去更新MainActivity的tv_main_number的内容,该数字显然与widget中的数字是一样的,都是RandomService的sRandomNumber。





 

0 0
原创粉丝点击