码迷,mamicode.com
首页 > 移动开发 > 详细

Android - SimpleAdapter适配器支持的组件及Listview模拟下载

时间:2015-08-29 21:46:28      阅读:263      评论:0      收藏:0      [点我收藏+]

标签:android   listview   源码   simpleadapter   adapter   

本来这周想写三篇的,结果这第一篇就不知道该如何起笔。语言表达能力真的需要提高啊。其实有好多想写的,最近这几天又接触到了以前听过但是没有去考虑的一些点。这篇的起因曾经做过一道题,我当时很不理解,我看有评论还是很多跟我当时想法一样的,一直没来得及去追究,终于还是放心不下,去看了一下,发现我错了。原题如下:

使用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。



版权声明:本文为博主原创文章,未经博主允许不得转载。

Android - SimpleAdapter适配器支持的组件及Listview模拟下载

标签:android   listview   源码   simpleadapter   adapter   

原文地址:http://blog.csdn.net/u014470702/article/details/48089771

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!