本来这周想写三篇的,结果这第一篇就不知道该如何起笔。语言表达能力真的需要提高啊。其实有好多想写的,最近这几天又接触到了以前听过但是没有去考虑的一些点。这篇的起因曾经做过一道题,我当时很不理解,我看有评论还是很多跟我当时想法一样的,一直没来得及去追究,终于还是放心不下,去看了一下,发现我错了。原题如下:
使用SimpleAdapter作为 ListView的适配器,行布局中支持下列哪些组件?
- TextView
- ProgressBar
- CompoundButton
- ImageView
当时我毫不犹豫的就选了ABCD,后来就一个红红的× 。答案是ACD。SimpleAdapter并不支持ProgressBar,我当时就很纳闷儿,为啥?
其实看看这个题,人家说的是SimpleAdapter,想当然的把SimpleAdapter当作了最基本的Adapter了(至于其他的适配器,自己可以看一下),所以就感觉答案应该是没问题的,那这个SimpleAdapter到底是支持什么?这就要从SimpleAdapter的源码看了,我把重要的摘出来看,首先看一下这个SimpleAdapter的构造函数。
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,int resource, String[] from, int[] to) { mData = data; mResource = mDropDownResource = resource; mFrom = from; mTo = to; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); }
这个SimpleAdapter只有一个构造函数,从参数可以看出,SimpleAdapter的数据资源类型还是比较固定的,List<? extends Map<String, ?>>。resource即每条记录的布局文件,from是data中Map的key ,而to 就是布局文件中的组件的id。
我们知道,Adapter的view显示或者view绑定是在getView函数里实现的,那么SimpleAdapter的getView函数,但是SimpleAdapter并没有直接重写getView,而是调用其他函数,最后调用了一个叫bindView的函数,从字面理解就是"绑定View"了,所以要看这个函数。
private void bindView(int position, View view) { final Map dataSet = mData.get(position); if (dataSet == null) { return; } final ViewBinder binder = mViewBinder; final String[] from = mFrom; final int[] to = mTo; final int count = to.length; for (int i = 0; i < count; i++) { final View v = view.findViewById(to[i]); if (v != null) { final Object data = dataSet.get(from[i]); String text = data == null ? "" : data.toString(); if (text == null) { text = ""; } boolean bound = false; if (binder != null) { bound = binder.setViewValue(v, data, text); } if (!bound) { if (v instanceof Checkable) { if (data instanceof Boolean) { ((Checkable) v).setChecked((Boolean) data); } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else { throw new IllegalStateException(v.getClass().getName() + " should be bound to a Boolean, not a " + (data == null ? "<unknown type>" : data.getClass())); } } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { setViewImage((ImageView) v, (Integer) data); } else { setViewImage((ImageView) v, text); } } else { throw new IllegalStateException(v.getClass().getName() + " is not a " + " view that can be bounds by this SimpleAdapter"); } } } } }
在bindView里有一长串的判断,就是我们要看的,
if (v instanceof Checkable) { //…… } else if (v instanceof TextView) { //…… } else if (v instanceof ImageView) { //…… } else { throw new IllegalStateException(v.getClass().getName() + " is not a " + " view that can be bounds by this SimpleAdapter"); }
可以看到首先判断这个view是不是实现的checkable(接口),然后判断是不是TextView,然后判断是不是ImageView,然后就没有然后了,如果以上三种都不是,那就抛异常了,是一个Illegal异常,不合法!异常的内容是 将要绑定的这个view 不是一个可以被SimpleAdapter绑定的View。所以再会过去看题,A、TextView , 可以;B、ProgressBar,什么鬼?不可以。C、CompoundButton,这个view实现了checkable接口,可以。D、ImageView,没问题。
所以答案就很明显了。
其实,SimpleAdapter简单易用并且也可以实现比较不错的布局,如果使用SimpleAdapter,我写了一个非常简单的数据源获得方式,如下:
private List<Map<String, Object>> getData() { <span style="white-space:pre"> </span>mData = new ArrayList<Map<String, Object>>(); for (int i = 1; i < 11; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("name", "name" + i + "\n" + "这里是该item的介绍"); map.put("button", "download"); map.put("image", R.drawable.ic_launcher); mData.add(map); } return mData; }
然后直接new一个SimpleAdapter就行了:
SimpleAdapter mAdapter = new SimpleAdapter(getApplicationContext(), getData(), R.layout.item, new String[] { "image", "name", "button" }, new int[] { R.id.id_item_image, R.id.id_item_text, R.id.id_item_btn });
至于布局文件就比较简单了。
这篇并不是多想说这个的,但是感觉已经说的很多了,还有好多废话。因为这个的原因,我很想模仿一下一些下载软件的app里的列表 下载的样式,其实就是把progress放在ListView里面,既然simpleAdapter不支持progressBar,那么就自己重写BaseAdapter。这次非常简单的重写了一下,在模拟下载时也没有使用service,也就是说并没有模拟后台下载,只是在当前activity里面模拟下载。
我重写的Adapter如下:
public class MyAdapter extends BaseAdapter { private List<Map<String, Object>> mData; private Context mContext; private LayoutInflater mInflater; public MyAdapter(Context context,List<Map<String, Object>> data) { this.mContext = context; this.mData = data; this.mInflater = LayoutInflater.from(context); } @Override public int getCount() { // TODO Auto-generated method stub return mData.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mData.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyView mView = null; ButtonListener mListener = null; if(convertView == null){ mView = new MyView(); convertView = mInflater.inflate(R.layout.item, null); mView.imageView = (ImageView) convertView.findViewById(R.id.id_item_image); mView.textView = (TextView) convertView.findViewById(R.id.id_item_text); mView.button = (Button) convertView.findViewById(R.id.id_item_btn); mView.progressBar = (ProgressBar) convertView.findViewById(R.id.id_item_progress); mListener = new ButtonListener(position, mView.progressBar); convertView.setTag(mView); }else{ convertView = (View) convertView.getTag(); } Map<String , Object> map = mData.get(position); mView.imageView.setImageResource((Integer) map.get("image")); mView.textView.setText((CharSequence) map.get("name")); mView.button.setText((CharSequence) map.get("button")); mView.progressBar.setProgress(0); mView.button.setOnClickListener(mListener); return convertView; } class MyView{ ImageView imageView; TextView textView; Button button; ProgressBar progressBar; } class ButtonListener implements OnClickListener{ Button button; private int mPosition; private ProgressBar mProgressBar; private int mProgress = 0; Thread mThread = null; private boolean flag = true; Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { button.setText("ok"); }; }; class DownloadThread extends Thread{ @Override public void run() { while(mProgress<=100&&flag){ mProgressBar.setProgress(mProgress); try { Thread.sleep(1000); mProgress += 2; } catch (InterruptedException e) { e.printStackTrace(); } } if(mProgressBar.getProgress()==100){ flag = false; handler.sendEmptyMessage(0); } } } public ButtonListener(int position,ProgressBar progressBar) { mPosition = position; mProgressBar = progressBar; } @Override public void onClick(View v) { button = (Button) v; if(button.getText().equals("download")){ button.setText("pause"); if(mThread==null){ mThread = new DownloadThread(); } mThread.start(); }else if(button.getText().equals("pause")&&mProgressBar.getProgress()<100){ mThread.interrupt(); mThread = null; flag = false; button.setText("go on"); }else if(button.getText().equals("go on")&&mProgressBar.getProgress()<100){ flag = true; if(mThread == null){ mThread = new DownloadThread(); } mThread.start(); button.setText("pause"); }else{ flag = false; mThread.interrupt(); mThread = null; button.setText("ok"); } } } }
这里其实并不难,只是有几个点需要注意一下:
1、给每一个item中的button绑定监听事件,在Listener的构造函数里面,我传入了当前item的position,以及该item中的ProgressBar,这个position在这里貌似没有用到。
2、在onClick里面开启新线程,模拟下载。其实应该是开启一个service,在service里面再开启新线程模拟下载,这样就是back掉当前的activity,下载依然在进行,这里只是简单的前台模拟。
3、当下载完毕后按钮上的文字改变时需要回到UI 线程也就是mainThread,否则会crash。
4、当mThread停止时,请不要使用stop这个方法,这个方法并没有什么卵用,使用后不管怎么stop,你会看到你的progressbar依然在跑,其实stop方法是在run函数运行完后才执行的。就算没有运行完你就调用了,他还是需要把run函数运行完。这里使用了interrupt()这个方法,使用后将自己的线程置空。当开启时,再重新new一个。并且在while判断的条件里多添加了一个标志变量,辅助控制下载。
运行的效果图:
好吧,界面有点丑,至于UI什么的请让美工设计师做吧。
至于怎么复写Adapter我就不说了,这不是这篇关注的问题。
我的代码写的并不好,如果有更好的方式,请告诉我,跪谢。
至于ListView的优化,我以后还会写一篇的。
这周的另外两篇,一篇关于fragment,一篇关于自定义view。
版权声明:本文为博主原创文章,未经博主允许不得转载。