android 中 AppWidget 的 ListView 的实现
http://www.cnblogs.com/debugman/archive/2012/06/18/android.html
3.0 以后系统直接支持了ListView. 关于ListView 的国内资料匮乏,大多数例子都是转来转去。由于初学android, 鄙人在搜索资料的时候遇到了不少麻烦~很是郁闷和苦恼~深感国内学习氛围确实怪异,学习方式需要改变。应该多去查看官方文档。。。。
话不多说,现在开始listView 实现:
这是文档列出的支持的布局和widget控件:
A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:
- FrameLayout
- LinearLayout
- RelativeLayout
And the following widget classes:
- AnalogClock
- Button
- Chronometer
- ImageButton
- ImageView
- ProgressBar
- TextView
- ViewFlipper
- ListView
- GridView
- StackView
- AdapterViewFlipper
Descendants of these classes are not supported
其中有ListView和 GridView 等控件。
android 中实现 appWidget 有自己的一套机制:
1. widget 的支持,AppWidgetProvider 类的实现。
覆盖在 AppWidgetProvider 的 OnReceive() 函数,从android的源码中可以知道,AppWidgetProvider 的 OnUpdate() , OnEnable(), OnDelete() 等方法都是从 OnReceive() 方法中分配进去的。即所有的广播先通过OnReceive()函数,再分配到OnUpdate()等函数去。
public void onReceive(Context context, Intent intent) { // Protect against rogue update broadcasts (not really a security issue, // just filter bad broacasts out so subclasses are less likely to crash). String action = intent.getAction(); if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null ) { int [] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); if (appWidgetIds != null && appWidgetIds.length > 0 ) { this .onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds); } } } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) { final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); this .onDeleted(context, new int [] { appWidgetId }); } } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) { this .onEnabled(context); } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) { this .onDisabled(context); } } // END_INCLUDE(onReceive) |
注意到:
String action = intent.getAction(); |
这里 intent 先获取 action, 通过action 来获取到广播并区分类型,所以自己定义 action 通过 PendingIntent 来实现各种跳转~
到这里先摆下基础的文件吧:
这里需要注意到的是:android中的xml 文件不能有大写字母。区分单词用最好用 _ 符号。否则找不到文件名。
provider_info 文件,提供 appWidget 的一些基本控制信息。
<?xml version= "1.0" encoding= "utf-8" ?> |
listview 文件: ListView 控件就在内部:
<?xml version= "1.0" encoding= "utf-8" ?> |
下面是list_item 文件:
<?xml version= "1.0" encoding= "utf-8" ?> |
list 的 item 中也可以添加 ImageView 等appWidget 支持的控件。
这是manifest 问件:
<?xml version= "1.0" encoding= "utf-8" ?> |
其中 service 提供 name 是 MyWidgetService ,他是继承RemoteViewsService 类。是我们需要为 远程 ListView 提供 数据源的服务。
RemoteViewsService的是个服务。其中:
public RemoteViewsFactory onGetViewFactory(Intent intent) { return new ListRemoteViewsFactory( this .getApplicationContext(), intent); } |
ListRemoteViewsFactory 这里就充当 ListView 的数据源。 |
就好比在activity 中使用ListView 一样。也需要通过AdapterView 来为ListView 提供数据源。不过AdatperView中提供了每一Item的方法。
具体逻辑如图:
下面是 service 的源代码:
public class MyWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new ListRemoteViewsFactory( this .getApplicationContext(), intent); } @Override public void onCreate() { // TODO Auto-generated method stub System.out.println( "service in onCreate" ); super .onCreate(); } @Override public void onDestroy() { // TODO Auto-generated method stub System.out.println( "service in onDestory" ); super .onDestroy(); } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub System.out.println( "service in onUnbind" ); return super .onUnbind(intent); } @Override public void onRebind(Intent intent) { // TODO Auto-generated method stub System.out.println( "service in onRebind" ); super .onRebind(intent); } @Override public void onStart(Intent intent, int startId) { // TODO Auto-generated method stub System.out.println( "service in onStart" ); super .onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub return super .onStartCommand(intent, flags, startId); } } |
class ListRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static int mCount = 0 ; private List new ArrayList private List new ArrayList private Context mContext; private int mAppWidgetId; private String url = "http://10.40.73.77/php/getData.php" ; public static int whichPage = 0 ; public static int mainPageId = - 1 ; public static int secPageId = - 1 ; public static final int mainPage = 0 ; public static final int secPage = 1 ; public static List new ArrayList //public static int[] checkPosArr = new int[100]; public ListRemoteViewsFactory(Context context, Intent intent){ mContext = context; mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } @Override public void onCreate() { System.out.println( "onCreate in factory" ); // TODO Auto-generated method stub try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public int getCount() { // TODO Auto-generated method stub if (whichPage == mainPage){ mCount = mWidgetItems.size(); } else if (whichPage == secPage){ mCount = mWidgetItemsAttr.size(); } return mCount; } @Override public long getItemId( int position) { // TODO Auto-generated method stub return position; } @Override public RemoteViews getLoadingView() { // TODO Auto-generated method stub System.out.println( "getLoadingView" ); return null ; } @Override public RemoteViews getViewAt( int position) { System.out.println( "getViewAt" ); // TODO Auto-generated method stub RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item); switch (whichPage){ case mainPage : if (- 1 == mainPageId){ //refresh main page System.out.println( "getViewAt mainPage refresh" ); rv.setTextViewText(R.id.item, mWidgetItems.get(position).text); Bundle extras = new Bundle(); extras.putInt( "page" , 0 ); extras.putInt(MyWidgetProvider.EXTRA_ITEM, position); extras.putString( "name" , mWidgetItems.get(position).text); Intent fillInIntent = new Intent(); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.item, fillInIntent); } else { //refresh to secPage list content System.out.println( "getViewAt mainPage item click" ); mainPageId = - 1 ; } break ; case secPage: if (- 1 == secPageId){ //refresh when click back button, but I only have one home button //refresh second list page System.out.println( "getViewAt secPage refresh" ); rv.setTextViewText(R.id.item, mWidgetItemsAttr.get(position)); rv.setImageViewResource(R.id.imageItem, R.drawable.checkbox); Bundle extras = new Bundle(); extras.putInt( "page" , 1 ); extras.putInt(MyWidgetProvider.EXTRA_ITEM, position); Intent fillInIntent = new Intent(); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.item, fillInIntent); rv.setOnClickFillInIntent(R.id.imageItem, fillInIntent); } else { //change positon rv.setTextViewText(R.id.item, mWidgetItemsAttr.get(position)); if (- 1 != checkPos.indexOf(position)){ //change list item picture to be checked rv.setImageViewResource(R.id.imageItem, R.drawable.checkedbox); } else { rv.setImageViewResource(R.id.imageItem, R.drawable.checkbox); } //每一个 item 都需要从新赋值。否则会出错!!具体原因没有查明 Bundle extras = new Bundle(); extras.putInt( "page" , 1 ); extras.putInt(MyWidgetProvider.EXTRA_ITEM, position); Intent fillInIntent = new Intent(); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.item, fillInIntent); rv.setOnClickFillInIntent(R.id.imageItem, fillInIntent); } break ; default : ; } return rv; } @Override public int getViewTypeCount() { // TODO Auto-generated method stub return 1 ; } @Override public boolean hasStableIds() { // TODO Auto-generated method stub return true ; } @Override public void onDataSetChanged() { // TODO Auto-generated method stub System.out.println( "onDataSetChanged" ); //this func is get data mWidgetItems.clear(); switch (whichPage){ case mainPage : System.out.println( "onDataSetChanged_mainPage" ); if (- 1 == mainPageId){ //refresh main page try { URL reqURL = new URL(url); BufferedReader br = new BufferedReader( new InputStreamReader(reqURL.openStream(), "gbk" )); StringBuffer sb = new StringBuffer(); String line; while ( null != (line = br.readLine())){ sb.append(line); } br.close(); ArrayList new ArrayList JSONArray arr_json = new JSONArray(sb.toString()); for ( int i = 0 , len = arr_json.length(); i < len; i++){ String strName = arr_json.getJSONObject(i).getString( "name" ); String strUrl = arr_json.getJSONObject(i).getString( "url" ); int id = arr_json.getJSONObject(i).getInt( "id" ); Map new HashMap mWidgetItems.add( new WidgetItem(strName, id)); map.put( "name" , strName); map.put( "url" , strUrl); mList.add(map); } mCount = mWidgetItems.size(); } catch (Exception e){ Toast.makeText(mContext, "can't connect server" , Toast.LENGTH_LONG).show(); } } else { // System.out.println( "onDataSetChanged_mainPage else" ); WidgetItem item = mWidgetItems.get(mainPageId); } System.out.println( "onDataSetChanged_-1" ); break ; case secPage: //here can get more info from server, but no need to get more infomation, if (- 1 == secPageId){ mWidgetItemsAttr.clear(); System.out.println( "onDataSetChanged_secPage -1" ); mWidgetItemsAttr.add( "zhang" ); mWidgetItemsAttr.add( "gui" ); mWidgetItemsAttr.add( "chuang" ); mWidgetItemsAttr.add( "hui" ); mWidgetItemsAttr.add( "cong" ); mWidgetItemsAttr.add( "gui" ); mWidgetItemsAttr.add( "chuang" ); mWidgetItemsAttr.add( "hui" ); mWidgetItemsAttr.add( "cong" ); mWidgetItemsAttr.add( "gui" ); mWidgetItemsAttr.add( "chuang" ); mWidgetItemsAttr.add( "hui" ); mWidgetItemsAttr.add( "cong" ); mWidgetItemsAttr.add( "cong" ); } else { System.out.println( "onDataSetChanged_secPage else" ); } break ; default : return ; } } @Override public void onDestroy() { System.out.println( "onDestory in factory" ); // TODO Auto-generated method stub mWidgetItems.clear(); } } |
|
public void onDataSetChanged(){.....} public RemoteViews getViewAt( int position) {....} public int getCount() {....} |
AppWidgetManager 的实例的方法: mgr.notifyAppWidgetViewDataChanged(appIds, R.id.myListView); 来更新要求更新数据源: |
onDataSetChanged(){.....} |
然后在调用 getViewAt( int position){....} 0 计数。 public RemoteViews getLoadingView(){...} 也可以。不过要注意放回的是RemoteViews 就说明这是要更新界面的,这个函数的作用就是在你更新界面的时候如果耗时就会显示 正在加载... 的默认字样,但是你可以更改这个界面。需要返回一个 RemoteViews 类型。其中你可以使用RemoteViews 去切换自己定义的 Layout 。 |
更多相关文章
- Android(安卓)UI(TextView)详解
- Android[高级教程] 设计模式之七 单例模式
- android的surfaceview的用法
- Android(安卓)AIDL 双向调用的使用及相关原理
- Android面试知识点总结-Android篇
- Android(安卓)Service
- 通用(任何android机型)Root教程(完整版!附砖机自救方法)
- 关闭 / 隐藏 Android(安卓)软键盘
- Android中View的滑动