widget中使用Listview (下)代码实现
来源:互联网 发布:php培训怎么样 编辑:程序博客网 时间:2024/05/29 12:46
1. widget的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tv_list_widget_title" android:layout_width="match_parent" android:layout_height="45dp" android:gravity="center" android:textAppearance="?android:attr/textAppearanceMedium" /> <!-- 通过代码控制,在ListView为空时,可以显示TextView --> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" ></ListView> <TextView android:id="@+id/list_empty" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="No Items Available" android:textSize="22sp" /> </FrameLayout></LinearLayout>
在widget中会使用一个ListView。所以,还需要提供Listview的Item布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:orientation="vertical" android:id="@+id/ll_widget_item" android:paddingLeft="10dp" android:gravity="center_vertical" > <TextView android:id="@+id/line1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/line2" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
需要注意的是,虽然item的布局文件与android.R.layout.simple_list_item_2很相似,但是如果ListView是用在widget中,最好不要使用系统提供的那些布局文件,因为在形成widget的视图时要先将其包装为RemoteViews,而RemoteViews中支持的视图组件是非常有限的,不能保证系统写的布局文件中用到的视图组件都可以被支持。
2. 提供一个元数据文件,里面声明该widget会需要一个配置界面(Configure Activity)
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"android:minWidth="110dp"android:minHeight="110dp"android:updatePeriodMillis="86400000"android:initialLayout="@layout/list_widget_layout"android:configure="com.example.widgettest.ListWidgetConfigureActivity"android:resizeMode="horizontal|vertical" />
configure属性声明了widget需要一个ConfigureActivity。在把widget拖放到桌面松手时的一刹那,会优先显示作为configure activity的ListWidgetConfigureActivity,当ListWidgetConfigureActivity以setResult(result_code,data)返回时,widget才会出现在界面上。
3. 修改AndroidManifest文件,声明为widget服务的AppWidgetProvider,声明作为widget的configure activity的ListWidgetConfigureActivity。同时,为widget中的ListView提供数据时,是不能使用Adapter的,需要利用RemoteViewsService,并提供一个RemoteViewsFactory实例来填充ListView的数据。
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <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> <activity android:name=".ListWidgetConfigureActivity" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter> </activity> <receiver android:name=".ListAppWidget" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter><meta-data android:name="android.appwidget.provider" android:resource="@xml/list_appwidget" /> </receiver> <service android:name=".ListWidgetService" android:permission="android.permission.BIND_REMOTEVIEWS" ></service> <service android:name=".MediaService" ></service> </application>
这里特别需要注意的是,凡是为widget提供帮助的receiver,activity和service,都需要添加一些额外的说明。
receiver标签中需要添加<intent-filter>和<meta-data>标签。名字是ListAppWidget的AppWidgetProvider类为该widget(s)服务。一个widget可以被反复拖拽到界面上,每拖拽一个在界面上显示,就意味着AppWidgetProvider需要多一个widget进行管理和通知。因此,一般情况下,AppWidgetProvider内的代码都需要以widget数组的形式来设计代码。
作为configure activity的activity中,必须要设置
<intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter>否则configure activity不会显示,app widget也不能创建
作为RemoteViewsService,service标签里面必须要声明 BIND_REMOTEVIEWS许可
另外,例子中还声明了一个<service>,这个service是用来读取手机媒体库中的图片和视频的
4. 新建ListWidgetConfigureActivity
首先是该Activity的布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv_configure_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="Select Media Type:" /> <RadioGroup android:id="@+id/rg_configure_mode" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_configure_title" android:orientation="vertical" > <RadioButton android:id="@+id/rb_mode_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="IMAGE" /> <RadioButton android:id="@+id/rb_mode_media" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="VIDEOS" /> </RadioGroup> <Button android:id="@+id/btn_configure" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Add Widget" android:layout_alignParentBottom="true" /></RelativeLayout>
然后是Activity的代码:
public class ListWidgetConfigureActivity extends Activity {private int mAppWidgetId;@ViewInject(R.id.rg_configure_mode)private RadioGroup mModeGroup;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.configure);ViewUtils.inject(this);//该Activity是被Intent启动的,//通过该Intent可以获得当前正要被创建的widget的IdmAppWidgetId = getIntent().getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);//如果用户在这个节目没有进行任何选择就直接按back退出该Activity的话//通过setResult(RESULT_CANCELED),widget就不会被创建setResult(RESULT_CANCELED);}@OnClick({ R.id.btn_configure })public void onAddClick(View v) {//通过当前widget的Id//为当前要被创建的widget设定一个Preference文件保存设置(显示图片还是视频)SharedPreferences sp = getSharedPreferences(String.valueOf(mAppWidgetId), MODE_PRIVATE);Editor editor = sp.edit();//将widget包装成一个RemoteViewsRemoteViews rv = new RemoteViews(getPackageName(),R.layout.list_widget_layout);switch (mModeGroup.getCheckedRadioButtonId()) {case R.id.rb_mode_image:editor.putString(ListWidgetService.KEY_MODE,ListWidgetService.MODE_IMAGE);editor.commit();rv.setTextViewText(R.id.tv_configure_title, "Image Collection");break;case R.id.rb_mode_media:editor.putString(ListWidgetService.KEY_MODE,ListWidgetService.MODE_MEDIA);editor.commit();rv.setTextViewText(R.id.tv_configure_title, "Media Collection");break;default:Toast.makeText(this, "please select a Media Type", 1).show();return;}Intent intent=new Intent(this, ListWidgetService.class);intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));//这里就是为widget中的ListView设置Adapter//需要提供被绑定的widget,该widget中需要被填充的ListView和为这个ListView服务的//可以启动RemoteViewsService的Intentrv.setRemoteAdapter(mAppWidgetId, R.id.list, intent);//如果ListView的数据为空,用什么视图组件来代替显示rv.setEmptyView(R.id.list, R.id.list_empty);//设置一个PendingItent,在widget的ListView中进行点击的时候触发Intent viewIntent=new Intent(Intent.ACTION_VIEW);PendingIntent pi=PendingIntent.getActivity(this, 0, viewIntent, 0);//setPendingIntentTemplate与setOnItemClickListener是类似的//这里使用setPendingIntentTemplate直接为ListView中的每一个Item都设置上了PendingIntentrv.setPendingIntentTemplate(R.id.list, pi);//利用AppWidgetManager来更新RemoteViewsAppWidgetManager manager=AppWidgetManager.getInstance(this);manager.updateAppWidget(mAppWidgetId, rv);//一切都设置完毕后,调用setResult(RESULT_OK,data);并把自己关闭//widget随即会出现在桌面上Intent data=new Intent();data.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);setResult(RESULT_OK,data);finish();}}
5. AppWidgetProvider用来接收widget的相关广播
public class ListAppWidget extends AppWidgetProvider {/** * widget中的内容需要更新的时候,会回调这个方法 */@Overridepublic void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {AppWidgetManager manager = AppWidgetManager.getInstance(context);//因为同一个widget可以被反复拖拽到页面,每拖拽一次就生成一个新的widget,会有一个新的id//所以,AppWidgetProvider大多数情况下都应该是面向数组进行操作for (int i = 0; i < appWidgetIds.length; i++) {Intent intent = new Intent(context, ListWidgetService.class);intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetIds[i]);intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));RemoteViews rv = new RemoteViews(context.getPackageName(),R.layout.list_widget_layout);//获取以widget的Id为名称的SharedPreferences文件SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetIds[i]), context.MODE_PRIVATE);String mode = sp.getString(ListWidgetService.KEY_MODE,ListWidgetService.MODE_IMAGE);if (ListWidgetService.MODE_MEDIA.equals(mode)) {rv.setTextViewText(R.id.tv_list_widget_title, "视频列表");} else {rv.setTextViewText(R.id.tv_list_widget_title, "图片列表");}//每一个widget都可以从ListWidgetService中获得数据rv.setRemoteAdapter(appWidgetIds[i], R.id.list, intent);//如果ListView中没有数据填充,那么就显示TextViewrv.setEmptyView(R.id.list, R.id.list_empty);Intent viewIntent = new Intent(Intent.ACTION_VIEW);PendingIntent pi = PendingIntent.getActivity(context, 0,viewIntent, 0);rv.setPendingIntentTemplate(R.id.list, pi);manager.updateAppWidget(appWidgetIds[i], rv);}}/** * 与该AppWidgetProvider绑定的若干个widget中,只要有一个widget被删除了 * AppWidgetProvider的 onDeleted会被回调 */@Overridepublic void onDeleted(Context context, int[] appWidgetIds) {for(int i=0;i<appWidgetIds.length;i++){SharedPreferences sp=context.getSharedPreferences(String.valueOf(appWidgetIds[i]), context.MODE_PRIVATE);Editor editor=sp.edit();editor.clear();editor.commit();}}/** * 第一个与该AppWidgetProvider绑定的widget被创建的时候,该方法会被回调 */@Overridepublic void onEnabled(Context context) {//启动用来读取媒体库中读取图片和视频的服务context.startService(new Intent(context, MediaService.class));}/** * 最后一个与该AppWidgetProvider绑定的widget被删除的时候,该方法会被回调 */@Overridepublic void onDisabled(Context context) {//停止用来读取媒体库中读取图片和视频的服务context.stopService(new Intent(context, MediaService.class));}}
6. 为widget中的ListView提供数据填充的RemoteViewsService
public class ListWidgetService extends RemoteViewsService{public static final String KEY_MODE="mode";public static final String MODE_IMAGE="image";public static final String MODE_MEDIA="media";//实现RemoteViewService时要实现的抽象方法,获得一个RemoteViewsFactory实例@Overridepublic RemoteViewsFactory onGetViewFactory(Intent intent) {return new ListRemoteViewFactory(this,intent);}/** * 内部类,实现RemoteViewsFatory接口 * 这个RemoteViewsFactory就相当于BaseAdatper */private class ListRemoteViewFactory implements RemoteViewsFactory{private Context mContext;private int mAppWidgetId;private Cursor mDataCursor;public ListRemoteViewFactory(Context context,Intent intent) {mContext=context;mAppWidgetId=intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);}@Overridepublic void onCreate() {//获得一个widget对应的SharedPerferences文件SharedPreferences sp=mContext.getSharedPreferences(String.valueOf(mAppWidgetId), Context.MODE_PRIVATE);//获得该widget的Mode类型String mode=sp.getString(KEY_MODE, MODE_IMAGE);//如果mode是视频类型,则去query系统的视频数据表获得对应的cursorif(MODE_MEDIA.equals(mode)){String[] projection={MediaStore.Video.Media.TITLE,MediaStore.Video.Media.DATE_TAKEN,MediaStore.Video.Media.DATA};mDataCursor = MediaStore.Images.Media.query(getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection);}else{//如果mode是图片类型,则去query系统的图片数据表获得对应的cursorString[] projection={MediaStore.Images.Media.TITLE,MediaStore.Images.Media.DATE_TAKEN,MediaStore.Images.Media.DATA};mDataCursor = MediaStore.Images.Media.query(getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection);}}@Overridepublic void onDataSetChanged() {mDataCursor.requery();}@Overridepublic void onDestroy() {mDataCursor.close();mDataCursor=null;}@Overridepublic int getCount() {return mDataCursor.getCount();}/** * 与BaseAdapter中的getView方法作用是一样的 */@Overridepublic RemoteViews getViewAt(int position) {mDataCursor.moveToPosition(position);//widget中的ListView中的Item也要包装为RemoteView//该代码与BaseAdapter的getView中获取Item布局的代码类似RemoteViews rv=new RemoteViews(getPackageName(),R.layout.list_widget_item_layout);rv.setTextViewText(R.id.line1, mDataCursor.getString(0));//DateFormat是一个安卓提供的工具类,可以省去用SimpleDateFormat去parse的步骤了rv.setTextViewText(R.id.line2, DateFormat.format("MM/dd/yyyy", mDataCursor.getLong(1)));SharedPreferences sp=mContext.getSharedPreferences(String.valueOf(mAppWidgetId), MODE_PRIVATE);String mode=sp.getString(KEY_MODE, MODE_IMAGE);String type;if(MODE_MEDIA.equals(mode)){type="video/*";}else{type="image/*";}Uri data=Uri.fromFile(new File(mDataCursor.getString(2)));Intent intent=new Intent();intent.setDataAndType(data, type);//setOnClickFillInIntent与 setPendingIntentTemplate可以联合使用//当在widgets中使用集合(比如说ListView, StackView等等),为一个个单独的Item设置PendingIntents是非常麻烦的,//通过设置PendingIntentsTemplate可以一次性给集合里面的Item设置相同的点击时动作,//如果希望某一个Item点击后的动作有所不同,就为这个Item单独使用fillInIntent来设置它点击后执行的Intent。rv.setOnClickFillInIntent(R.id.ll_widget_item, intent);return rv;}/** * 在getView方法执行获得View的过程中,该方法的返回值会作为等待加载画面一直显示 * 当getView方法返回时,等待加载画面会自动消失 */@Overridepublic RemoteViews getLoadingView() {return null;}/** * 该方法与BaseAdapter中的getViewTypeCount的意思是一样的 */@Overridepublic int getViewTypeCount() {return 1;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic boolean hasStableIds() {return false;}}}
7. 最后是一个服务类型的service MediaService。它的启动和中止是在AppWidgetProvider的onEnable和onDisable中进行的。这个服务的作用就是当你添加或者删除了图片或视频的时候,这个MediaService上有一个ContentObserver,会即时把这个变化反应到widget的Listview上。及可以实时保持widget的ListView中的内容与手机媒体库中的内容保持一致
public class MediaService extends Service{private ContentObserver mMediaStoreObserver;@Overridepublic void onCreate() {super.onCreate();mMediaStoreObserver=new ContentObserver(new Handler()) {@Overridepublic void onChange(boolean selfChange) {Context _context=MediaService.this;AppWidgetManager manager=AppWidgetManager.getInstance(_context);ComponentName provider=new ComponentName(_context, ListAppWidget.class);//获得,所有利用AppWidgetProvider作为广播接收器的那些widget的idint[] ids=manager.getAppWidgetIds(provider);//这样当有数据发生变化时,这种变化会反映到所有桌面上的widget的ListView列表中manager.notifyAppWidgetViewDataChanged(ids, R.id.list);}};getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, mMediaStoreObserver);getContentResolver().registerContentObserver(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, mMediaStoreObserver);}@Overridepublic void onDestroy() {super.onDestroy();getContentResolver().unregisterContentObserver(mMediaStoreObserver);}@Overridepublic IBinder onBind(Intent intent) {return null;}}
所有的代码和配置文件都书写完毕。
0 0
- widget中使用Listview (下)代码实现
- 2015.4.7-4.8 widget中使用Listview (上)分析说明
- WIDGET滚动设置示例代码(例中WIDGET绑定在IUTKFORM中)
- Widget中使用js实现页面的滑动效果
- widget.ListView
- widget开发(下)
- iOS 开发之Widget的开发及使用(下)
- widget中使用动画效果
- widget中使用动画效果
- Widget中Layout的使用
- 在widget实现复杂布局(Listview,GirdView)以及RemoteViewsService、RemoteViewsFactory的用法
- ListView中使用线程实现无限加载
- Android中使用ListView实现自适应表格
- ListView中使用CheckedTextView实现多选
- ListView 中使用RadioButton实现单选
- ListView 中使用CheckBox实现多选
- 基础ListView 实现代码
- UiAutomator中android.widget.ListView控件的子元素遍历
- spring的HibernateDaoSupport、HibernateTemplate、jdbcTemplate的区别
- MySQL日期时间函数大全 转
- HTML5 LocalStorage 本地存储
- struts2 启动tomcat时报错:org.apache.catalina.core.StandardContext filterStart
- Python基础学习篇——Global全局变量的使用
- widget中使用Listview (下)代码实现
- Win10手机预览版第二次重大更新本周六推送
- 2015.4.9学习mybatis
- 如何使用触发器实现IP限制用户登录
- 个人开发者也能盈利!Bmob支付SDK使用实例
- 谈谈我对读研和软件学院的看法(转自网络,作者不太清楚)
- leetcode || 72、Edit Distance
- 2016年tencent实习生笔试部分试题分析
- 临行前的感悟,我的曾经与SOVO同行的日子