前段在EOE上讨论了延迟加载,然后做了个,模拟延迟加载的List.估计以后会用到吧.毕竟List还是经常遇到的.
UI线程必须是自己更新,不能由其它线程来更新,这个问题已经很多人讨论过了.而Android使用Handler的HandleMessage来处理把控制返回给UI线程.如果直接一个Runnable是不行的.
由于没有到网络上去载图片,所以线程睡眠了500毫秒来模拟.在这里可以用一个线程去下载图片,然后Notify通知图片已经准备好了.
如果有不对的喜欢指正,讨论.把代码放上来.
有个小问题:在开始时显示的是未选中的图片,没有处理初始化加载图片,而主要关注的是滚动时加载图片.
- package com.me.list;
- import java.util.ArrayList;
- import java.util.HashMap;
- import android.app.Activity;
- import android.app.ListActivity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Looper;
- import android.os.Message;
- import android.util.Log;
- import android.view.ContextMenu;
- import android.view.MenuItem;
- import android.view.View;
- import android.view.ContextMenu.ContextMenuInfo;
- import android.view.LayoutInflater;
- import android.view.View.OnCreateContextMenuListener;
- import android.view.ViewGroup;
- import android.widget.AbsListView;
- import android.widget.AbsListView.OnScrollListener;
- import android.widget.AdapterView;
- import android.widget.ListView;
- import android.widget.SimpleAdapter;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.ArrayAdapter;
- import android.widget.BaseAdapter;
- import android.widget.ImageView;
- import android.widget.TextView;
- import com.me.*;
- import java.util.List;
- /**
- *
- * @author archko
- */
- public class ListViewLazy3 extends ListActivity implements OnScrollListener{
- private TextView mStatus;
- private boolean mBusy= false ;
- //private boolean needToUpdate=false;
- private static final String TAG= "List13" ;
- private AbsListView absListView;
- private ArrayList<HashMap<String,Object>> users= new ArrayList<HashMap<String,Object>>();
- private Bitmap mIcon1, mIcon2;
- private int firstPos= 0 ;
- private NoLooperThread noLooerThread;
- private MySimpleAdapter adapter;
- @Override
- public void onCreate(Bundle savedInstanceState){
- super .onCreate(savedInstanceState);
- setContentView(R.layout.list_13);
- mStatus=(TextView)findViewById(R.id.status);
- mStatus.setText("Idle" );
- for ( int i= 0 ; i< 30 ; i++){
- HashMap<String,Object> user=new HashMap<String,Object>();
- user.put("img" ,R.drawable.checkoff);
- user.put("username" , "姓名(" +i+ ")" );
- user.put("age" ,( 20 +i)+ "" );
- users.add(user);
- }
- // Use an existing ListAdapter that will map an array
- // of strings to TextViews
- adapter=new MySimpleAdapter( this );
- setListAdapter(adapter);
- getListView().setOnScrollListener(this );
- }
- //这个方法只要在列表中移动就会调用.
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
- int lastItem=firstVisibleItem+visibleItemCount- 1 ;
- Log.d(TAG,"firstPos." +firstPos+ " *firstVisibleItem." +firstVisibleItem); //这个位置不太正确,在我这总是会差一个
- if (firstVisibleItem!=firstPos){
- firstPos=firstVisibleItem;
- //needToUpdate=true;
- //if(needToUpdate){ //需要更新,启动线程
- this .absListView=view;
- //Log.d(TAG,"noLooperThread."+noLooerThread+" isAlive."+noLooerThread.isAlive());
- if (noLooerThread!= null ){
- //if(noLooerThread.isAlive()){
- //noLooerThread.stop();
- noLooerThread.interrupt();
- Log.d(TAG,"onScroll.noLooerThread.interrupt" );
- noLooerThread=null ;
- }
- noLooerThread=new NoLooperThread();
- noLooerThread.start();
- //}
- }
- }
- //为什么会对称出现呢?更新5个就是从头开始5个和从尾开始5个?
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState){
- //没有使用这个,因为这是在鼠标拖动时才会发生,而上面的只要焦点变化都会触发.
- //Log.d(TAG,"scrollState."+scrollState);
- /*switch(scrollState){
- case OnScrollListener.SCROLL_STATE_IDLE:
- mBusy=false;
- this.absListView=view;
- noLooerThread=new NoLooperThread();
- noLooerThread.start();
- mStatus.setText("Idle");
- break;
- case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
- mBusy=true;
- mStatus.setText("Touch scroll");
- break;
- case OnScrollListener.SCROLL_STATE_FLING:
- mBusy=true;
- mStatus.setText("Fling");
- break;
- }*/
- }
- //更新List里的项,这里只作通知数据更新了.
- public void update(){
- Log.d(TAG,"update." );
- adapter.notifyDataSetChanged();
- }
- ViewHolder holder;
- class MySimpleAdapter extends BaseAdapter{
- private LayoutInflater mInflater;
- //private Bitmap mIcon1, mIcon2;
- private Context ctx;
- public MySimpleAdapter(Context c){
- mInflater=LayoutInflater.from(c);
- ctx=c;
- // Icons bound to the rows.
- mIcon1=BitmapFactory.decodeResource(c.getResources(),R.drawable.checkon);
- mIcon2=BitmapFactory.decodeResource(c.getResources(),R.drawable.checkoff);
- }
- public int getCount(){
- return users.size();
- }
- public Object getItem( int position){
- return position;
- }
- public long getItemId( int position){
- return position;
- }
- //ViewHolder holder;
- //而convertView只有在Activity初始化时才会是空.
- @Override
- // <editor-fold defaultstate="collapsed" desc="comment">
- public View getView( int position,View convertView,ViewGroup parent){
- if (convertView== null ){
- convertView=mInflater.inflate(R.layout.list_items,null );
- // Creates a ViewHolder and store references to the two children views
- // we want to bind data to.
- holder=new ViewHolder();
- holder.itemTitle=(TextView)convertView.findViewById(R.id.ItemTitle);
- holder.itemText=(TextView)convertView.findViewById(R.id.ItemText);
- holder.itemIcon=(ImageView)convertView.findViewById(R.id.ItemImage);
- //holder.itemIcon.setImageBitmap(mIcon2);
- convertView.setTag(holder);
- }else {
- // Get the ViewHolder back to get fast access to the TextView and the ImageView.
- holder=(ViewHolder)convertView.getTag();
- }
- holder.itemTitle.setText(users.get(position).get("username" ).toString());
- holder.itemText.setText(users.get(position).get("age" ).toString());
- int i=Integer.valueOf(users.get(position).get( "img" ).toString());
- holder.itemIcon.setImageBitmap(BitmapFactory.decodeResource(ctx.getResources(),i));
- //Log.d(TAG,"getView.setIcon");
- return convertView;
- }// </editor-fold>
- }
- static class ViewHolder{
- TextView itemTitle, itemText;
- ImageView itemIcon;
- }
- class EventHandler extends Handler{
- public EventHandler(Looper looper){
- super (looper);
- }
- public EventHandler(){
- super ();
- }
- public void handleMessage(Message msg){
- //可以根据msg.what执行不同的处理,这里没有这么做
- switch (msg.what){
- case 1 :
- Log.d(TAG,"1.EventHandler.handleMessage.before" );
- update();
- Log.d(TAG,"1.EventHandler.handleMessage.after" );
- break ;
- case 2 :
- Log.d(TAG,"2.EventHandler.handleMessage.before" );
- update();
- Log.d(TAG,"2.EventHandler.handleMessage.after" );
- mBusy=true ;
- //needToUpdate=false;
- break ;
- case 3 :
- //不能在非主线程的线程里面更新UI,所以这里通过Log打印收到的消息
- Log.e(TAG,(String)msg.obj);
- //ownLooperThread.stop();
- break ;
- default :
- //不能在非主线程的线程里面更新UI,所以这里通过Log打印收到的消息
- Log.e(TAG,(String)msg.obj);
- break ;
- }
- }
- }
- //这个Thread可以不用这么复杂的.因为当时还没明白Handler,Message所以也是借用别人的代码.简单点就是new Runnable(){run(){
- XXX..........
- Message msg=new Message();
- msg.what=XYZ..............;
- mHandler.sendMessage(msg);
- }}
- Android为每个线程产生一个Looper这个在程序异常时看Logcat就会明白的.
- class NoLooperThread extends Thread{
- private EventHandler mNoLooperThreadHandler;
- private boolean loadIngImg= true ; //正在加载图片
- private boolean needToUpdate= false ; //是否需要更新图片列表
- private Object lock= new Object();
- // <editor-fold defaultstate="collapsed" desc="comment">
- class InnerThread extends Thread{
- public void run(){
- //Log.d(TAG,"NoLooper.run.loadImg.run");
- int count=absListView.getChildCount();
- int first=absListView.getFirstVisiblePosition();
- boolean flag= false ;
- for ( int i= 0 ; i<count ; i++){
- try {
- Thread.sleep(500 );
- }catch (InterruptedException ex){
- Thread.interrupted();//虽然这里捕获到异常了,不过,它还是会继续执行的.线程不会停止,应该设置共享变量来停止非阻塞的线程,暂时没有使用.
- }
- if (Integer.valueOf(users.get(first+i).get( "img" ).toString())==R.drawable.checkoff){
- users.get(first+i).put("img" ,R.drawable.checkon);
- Log.d(TAG,"thead." +Thread.currentThread()+ " update ." +(first+i));
- flag=true ;
- }
- }
- synchronized (lock){
- lock.notify();
- loadIngImg=false ; //加载完毕
- if (flag){
- needToUpdate=true ; //加载后需要更新
- }
- Log.d(TAG,"NoLooper.run.loadImg.notify" );
- }
- }
- }// </editor-fold>
- public void run(){
- InnerThread it=new InnerThread();
- synchronized (lock){
- it.start();
- while (loadIngImg){
- try {
- lock.wait();
- }catch (InterruptedException e){
- loadIngImg=false ;
- it.interrupt();
- }
- Log.d(TAG,"needToUpdate." +needToUpdate);
- if (needToUpdate){ //需要更新列表,启动线程.
- Looper myLooper, mainLooper;
- myLooper=Looper.myLooper();
- mainLooper=Looper.getMainLooper(); //这是一个static函数
- String obj;
- if (myLooper== null ){
- mNoLooperThreadHandler=new EventHandler(mainLooper);
- obj="NoLooperThread has no looper and handleMessage function executed in main thread!" ;
- }else {
- mNoLooperThreadHandler=new EventHandler(myLooper);
- obj="This is from NoLooperThread self and handleMessage function executed in NoLooperThread!" ;
- }
- Log.d(TAG,"NoLooperThread.run.mNoLooperThreadHandler" );
- mNoLooperThreadHandler.removeMessages(0 );
- //send message to main thread:
- //what=2 when=0 arg1=1 arg2=1 obj=NoLooperThread has no looper and handleMessage
- //function executed in main thread!
- Message m=mNoLooperThreadHandler.obtainMessage(2 , 1 , 1 ,obj);
- mNoLooperThreadHandler.sendMessage(m);
- }
- loadIngImg=true ;
- }
- }
- }
- }
- }
- list_13这个只是一个ListView和一个文本:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
- <ListView android:id="@android:id/list"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:drawSelectorOnTop="false" />
- <TextView android:id="@+id/status"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="8dip"
- android:paddingRight="8dip" />
- </LinearLayout>
- 行布局只是一个ImageView和两个TextView,没有特别的.
- 使用Asynctask 是Android专门为线程处理而写的类,封装了一些方法,更易于使用的.