一个令人无比蛋疼的线程里更新ListView的问题
弄了好几天了,想实现一个功能:显示一个ListView,在Activity的Create方法中开启一个新的线程,在线程里通过循环为一个自定义的ArrayAdapter类的add(使用父类的方法)方法增加数据,ListView已经使用setAdapter方法与自定义的ArrayAdapter绑定,希望在线程里为ArrayAdapter增加数据的同时,显示的ListView能同步显示出增加的数据,可是这么做后,会报Only the original thread that created a view hierarchy can touch its views的错误,并出现Force Close
最终希望达到的效果就是,ListView随着线程里ArrayAdapter数据项的逐步增加(故意增加延迟放慢循环),ListView一条一条的显示数据,并不影响主界面的其他操作。
我想可能是我的思路有问题,希望能熟悉Android开发的朋友们能帮帮我,谢谢了。
另外这个代码的ListView只能显示出ArrayAdapter的前10条数据的重复组合,虽然数据的总数貌似是对的,但是ListView的显示的是ArrayAdapter的前10条数据的重复随机组合,为什么会出现这种情况呢?
再次感谢各位了!谢谢!
贴出全部代码:
主Activity代码:
ListTest.java:
- Java code
public class ListTest extends Activity { private ListView lv; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); lv = (ListView)findViewById(R.id.list); //创建新线程用来显示列表 HandlerThread handlerThread = new HandlerThread("hthread_readcontact"); handlerThread.start(); //异步启动线程 //创建Handler MyHandler myHandler = new MyHandler(handlerThread.getLooper()); //创建消息对象 Message msg = myHandler.obtainMessage(); //取出消息并执行 msg.sendToTarget(); } //循环增加100个列表项 protected void AddListItem(){ List<PeopleInfo> myList = new ArrayList<PeopleInfo>(); MyAdapter ca = new MyAdapter(ListTest.this,myList); //ca.setNotifyOnChange(true); lv.setAdapter(ca); for(int i=1;i<=100;i++){ //增加记录 PeopleInfo pe = new PeopleInfo(); pe.setPeoleName("Fucker"+Integer.toString(i)); pe.setPeopleNum(Integer.toString(i)); System.out.println("i:"+Integer.toString(i)); System.out.println("PeopleInfo-->Name:"+pe.peopleName); System.out.println("PeopleInfo-->Num :"+pe.peopleNum); ca.add(pe); SystemClock.sleep(50L); //为了看到效果,故意增加一个延迟 } ca.notifyDataSetChanged(); } class MyHandler extends Handler{ public MyHandler(){ } public MyHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { AddListItem(); System.out.println("Thread is runed!"); } } }其他类代码:
MyAdapter.java:
- Java code
public class MyAdapter extends ArrayAdapter<PeopleInfo> { private LayoutInflater inflater; List<PeopleInfo> itemList; private static final int mResource = R.layout.contact_list; //xml布局文件 public MyAdapter(Context context,List<PeopleInfo> objects) { super(context, mResource, objects); // TODO Auto-generated constructor stub inflater = LayoutInflater.from(context); itemList = objects; } @Override public void add(PeopleInfo object) { // TODO Auto-generated method stub super.add(object); //itemList.add(object); } @Override public void setNotifyOnChange(boolean notifyOnChange) { // TODO Auto-generated method stub super.setNotifyOnChange(notifyOnChange); } @Override public Filter getFilter() { // TODO Auto-generated method stub return super.getFilter(); } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder holder; holder = new ViewHolder(); if(convertView == null){ convertView = inflater.inflate(R.layout.contact_list, null); //convertView = inflater.inflate(R.layout.listitem, null); holder.PhoneName = (TextView)convertView.findViewById(R.id.mname); holder.PhoneNum = (TextView)convertView.findViewById(R.id.msisdn); convertView.setTag(holder); holder.PhoneName.setText(itemList.get(position).getPeopleName()); holder.PhoneNum.setText(itemList.get(position).getPeopleNum()); } return convertView; } class ViewHolder{ TextView PhoneName; TextView PhoneNum; }}
PeopleInfo.java:
- Java code
public class PeopleInfo implements Serializable { public String peopleName; public String peopleNum; public String getPeopleName(){ return peopleName; } public void setPeoleName(String peopleName){ this.peopleName=peopleName; } public String getPeopleNum(){ return peopleNum; } public void setPeopleNum(String peopleNum){ this.peopleNum = peopleNum; }}[解决办法]
如果是我,我想我会这么写。不知道是不是符合你的要求。
- Java code
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mListView = (ListView)findViewById(R.id.list); new Thread(new Runnable() { public void run() { mHandler.postDelayed((new Runnable() { public void run() { AddListItem(); } }), 1000); } }).start(); }
[解决办法]
还是给个例子吧,你这用法感觉非常奇怪(我说楼主):
- Java code
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayAdapter<String> ad = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, new Vector<String>()); mlv = new ListView(this); mlv.setAdapter(ad); setContentView(mlv); mh = new Handler(); new Thread() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 5; ++i) { First_Android.this.mh.post(new Runnable() { public void run() { // TODO Auto-generated method stub int n = new Random().nextInt(); @SuppressWarnings("unchecked") ArrayAdapter<String> ad = (ArrayAdapter<String>) First_Android.this.mlv.getAdapter(); ad.add(Integer.toString(n)); ad.notifyDataSetChanged(); } }); try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start();
[解决办法]
按照你的要求修改了一下,你代码最大的问题就是在Handler的处理过程中加入了Sleep,等于把主线程阻塞了,导致界面绘制不畅。
- Java code
public class ListTest extends Activity { private ListView mListView; Handler mHandler = new Handler(); List<PeopleInfo> mList = null; MyAdapter mAdapter = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mListView = (ListView)findViewById(R.id.list); mList = new ArrayList<PeopleInfo>(); mAdapter = new MyAdapter(ListTest.this, mList); mListView.setAdapter(mAdapter); new Thread(new Runnable() { public void run() { while (true) { mHandler.post(new Runnable() { public void run() { AddListItem(); } }); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); } // 循环增加100个列表项 protected void AddListItem() { // 增加记录 PeopleInfo pe = new PeopleInfo(); int number = (int)(Math.random() * 1000); pe.setPeoleName("Haha " + Integer.toString(number)); pe.setPeopleNum(Integer.toString(number)); System.out.println("i:" + Integer.toString(number)); System.out.println("PeopleInfo-->Name:" + pe.peopleName); System.out.println("PeopleInfo-->Num :" + pe.peopleNum); mAdapter.add(pe);// SystemClock.sleep(1000); // 为了看到效果,故意增加一个延迟 mAdapter.notifyDataSetChanged(); }}
[解决办法]
ListTest:
public class ListTest extends Activity {
//-------------------------------
private Thread myThread = new Thread(){
@Override
public void run () {
AddListItem();
}
};
//------------------------------
private MyHandler myHandler = new MyHandler();
private ListView lv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView)findViewById(R.id.list);
// //创建新线程用来显示列表
// HandlerThread handlerThread = new HandlerThread("hthread_readcontact");
// handlerThread.start(); //异步启动线程
// //创建Handler
// MyHandler myHandler = new MyHandler(handlerThread.getLooper());
// //创建消息对象
// Message msg = myHandler.obtainMessage();
//
// //取出消息并执行
// msg.sendToTarget();
//----------------------------------
List<PeopleInfo> myList = new ArrayList<PeopleInfo>();
MyAdapter ca = new MyAdapter(this, myList);
lv.setAdapter(ca);
myThread.start();
//------------------------------------
}
//循环增加100个列表项
protected void AddListItem(){
// List<PeopleInfo> myList = new ArrayList<PeopleInfo>();
// MyAdapter ca = new MyAdapter(ListTest.this,myList);
// //ca.setNotifyOnChange(true);
// lv.setAdapter(ca);
for(int i=1;i<=100;i++){
//增加记录
// PeopleInfo pe = new PeopleInfo();
// pe.setPeopleName("Fucker"+Integer.toString(i));
// pe.setPeopleNum(Integer.toString(i));
// System.out.println("i:"+Integer.toString(i));
// System.out.println("PeopleInfo-->Name:"+pe.peopleName);
// System.out.println("PeopleInfo-->Num :"+pe.peopleNum);
//-----------------------------------
Message m = new Message();
Bundle data = new Bundle();
data.putString("name", "Fucker" + i);
data.putString("number", "" + i);
m.setData(data);
myHandler.sendMessage(m);
//------------------------------------
SystemClock.sleep(200L); //为了看到效果,故意增加一个延迟
}
//ca.notifyDataSetChanged();
}
class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
// AddListItem();
// System.out.println("Thread is runed!");
//----------------------------------
if (null == msg)
return;
PeopleInfo pe = new PeopleInfo();
Bundle data = msg.getData();
pe.setPeopleName(data.getString("name"));
pe.setPeopleNum(data.getString("number"));
Log.v("================", data.getString("name")+" "+data.getString("number"));
MyAdapter ad = (MyAdapter) lv.getAdapter();
ad.add(pe);
ad.notifyDataSetChanged();
//-------------------------------------
}
}
}
MyAdapter:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
if(convertView == null){
ViewHolder holder;
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.contact_list, null);
//convertView = inflater.inflate(R.layout.listitem, null);
holder.PhoneName = (TextView)convertView.findViewById(R.id.mname);
holder.PhoneNum = (TextView)convertView.findViewById(R.id.msisdn);
convertView.setTag(holder);
holder.PhoneName.setText(itemList.get(position).getPeopleName());
holder.PhoneNum.setText(itemList.get(position).getPeopleNum());
}
//-------------------------------------
else {
ViewHolder h = (ViewHolder) convertView.getTag();
h.PhoneName.setText(itemList.get(position).getPeopleName());
h.PhoneNum.setText(itemList.get(position).getPeopleNum());
}
//-------------------------------------
return convertView;
}