【问题标题】:ListView and Adapter with a large number of items具有大量项目的 ListView 和 Adapter
【发布时间】:2012-12-12 10:26:56
【问题描述】:

我是安卓编程的新手。在对 ListView 和 ArrayAdapter 进行了一些研究之后,我决定编写一个简单的演示程序来练习。

我想显示我的自定义视图样式 ListView,所以我重写了 ArrayAdapter 的 getView() 函数。我知道为了防止内存泄漏,应该检查参数convertView,并且只有在convertView为null时才膨胀新对象。我还制作了一个 AsyncTask 以在后台(在 doInBackground 中)继续将数据添加到 ArrayAdapter 中,并将 notifyDataChanged 保留为 ListView(在 onProgressUpdate() 中)。

问题是:当我设置 MAX_ITEM=50(这意味着我将在 AsyncTask 中添加多少数据)时,一切正常。但是当我设置 MAX_ITEM=500 时,logcat 显示错误消息:“Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.” 并且应用程序关闭下。谁能告诉我问题出在哪里?以下是我的源代码“MyTest.java”和我的布局xml文件“main.xml”和“layout_row.xml”,感谢大家的观看和帮助。

MyTest.Java:

public class MyTest extends Activity  { 

private static final String TAG="Tany";
private static final int MAX_ITEM = 50;
private MyArrayAdapter adapter;
private ListView listview;

private int addCounter = 0;

public class MyData implements Comparable<MyData>{

    public String str1;
    public String str2;
    public String str3;

    public MyData(String str1, String str2, String str3){                   
        this.str1 = str1;
        this.str2 = str2;
        this.str3 = str3;
    }

    @Override
    public int compareTo(MyData data) {
        // TODO Auto-generated method stub

        int result = this.str1.compareTo(data.str1);
        return result;
    }
}

public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     listview = (ListView)findViewById(R.id.listview);

     adapter = new MyArrayAdapter(this, R.layout.layout_row);

     //set adapter
     listview.setAdapter(adapter);
     listview.setOnItemClickListener(new AdapterView.OnItemClickListener(){

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // TODO Auto-generated method stub
            Log.d(TAG, "position "+position+" is clicked");                             
            Toast.makeText(parent.getContext(), adapter.getItem(position).str1+", "+adapter.getItem(position).str2, Toast.LENGTH_SHORT).show();                             
        }           
     });         

     listview.setTextFilterEnabled(true);

}

public void onStart(){
    super.onStart();                                
    MyTask newTask = new MyTask();
    newTask.execute();      
}

public class MyTask extends AsyncTask<Void, Void, Void>{

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        for(int i = addCounter; i<MAX_ITEM; i++)
        {
            adapter.add(new MyData("str1="+i,"str2="+i,"str3="+i));
            addCounter++;               
            publishProgress();                              
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... progress ){         
        adapter.notifyDataSetChanged();
    }

    @Override
    protected void onPostExecute(Void result){                      
        Log.d(TAG, "AsyncTask MyTask finsish its work");                        
    }       
}


public class MyArrayAdapter extends ArrayAdapter<MyData>{       

    public MyArrayAdapter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
        // TODO Auto-generated constructor stub                                     
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {         
        View row;                   
        if(convertView == null){
            LayoutInflater inflater = getLayoutInflater();
            row = inflater.inflate(R.layout.layout_row, parent, false);
        }else{
            Log.d(TAG,"recycle view, pos="+position);
            row = convertView;
        }

        TextView tv1 = (TextView) row.findViewById(R.id.textView1);         
        tv1.setText(    ((MyData)getItem(position)).str1    );

        TextView tv2 = (TextView) row.findViewById(R.id.textView2);
        tv2.setText(    ((MyData)getItem(position)).str2    );

        TextView tv3 = (TextView) row.findViewById(R.id.textView3);
        tv3.setText(    ((MyData)getItem(position)).str3    );

        return row;
    }                   
}   
}

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/vertical_container" 
android:orientation="vertical"
android:layout_width="fill_parent" 
android:layout_height="fill_parent">

<TextView 
        android:text="@string/list_title_start"             
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content">
</TextView>

<ListView 
    android:id="@+id/listview"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content">        
</ListView>

<TextView 
        android:text="@string/list_title_end"           
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content">
</TextView>
</LinearLayout>

layout_row.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical" >    

<LinearLayout 
    android:layout_height="wrap_content" 
    android:layout_width="wrap_content" 
    android:id="@+id/linearLayout1" 
    android:orientation="horizontal">
    <ImageView 
        android:layout_height="wrap_content"
        android:id="@+id/imageView1" 
        android:layout_width="wrap_content" 
        android:src="@drawable/ic_launcher">        
    </ImageView>
    <TextView 
        android:text="TextView1" 
        android:id="@+id/textView1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginLeft="6dip"
        android:layout_marginTop="6dip" 
        android:textAppearance="?android:attr/textAppearanceLarge">
    </TextView>
</LinearLayout>
<LinearLayout 
    android:layout_height="wrap_content" 
    android:layout_width="wrap_content" 
    android:id="@+id/linearLayout1" 
    android:orientation="horizontal">
    <TextView 
        android:id="@+id/textView2" 
        android:text="TextView" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:textAppearance="?android:attr/textAppearanceSmall">
    </TextView>
    <TextView 
        android:text="TextView" 
        android:id="@+id/textView3" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:textAppearance="?android:attr/textAppearanceSmall" 
        android:layout_marginLeft="6dip">
    </TextView>
</LinearLayout>    
</LinearLayout>

【问题讨论】:

  • 因为ArrayAdapter.add(obj) 在内部调用notifyDataSetChanged() ... 通过publishProgress 发送新的MyData 并将其添加到onProgressUpdate 中的适配器
  • 感谢您的建议!我尝试在 onProgressUpdate 中修改添加操作,当我将 MAX_ITEM 设置为 2000 时它工作得很好:)

标签: java android


【解决方案1】:

正如 Selvin 所说,您正在从在另一个线程上运行的方法 doInBackground 修改您的 Views。在 android 中这是被禁止的,你也应该从 onPostExecute 方法修改视图。

【讨论】:

  • 感谢您的建议!我尝试在 onProgressUpdate 中修改添加操作,它可以工作!但是现在我还有两个问题: 1. 为什么当 MAX_ITEM=50 时,不会出现这样的错误? 2.这种实现方式有什么缺点吗?
猜你喜欢
  • 2021-03-11
  • 2020-07-07
  • 1970-01-01
  • 2023-04-03
  • 2014-09-16
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 2015-07-25
相关资源
最近更新 更多