編輯:關於Android編程
前段時間完成某項目離線數據部分時,用了大量ListView顯示加載數據,在碰到大數據量的加載時容易出現的錯誤。即我們將List作為參數傳遞給ListView的Adapter,同時在開辟線程加載數據時直接對剛剛參數傳遞的List操作,具體代碼如下:
[java] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px">package com.jackchan.listtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class ListTestActivity extends Activity {
private ListView mListView;
private Button mBtn;
private int text = 0;
private List<String> mList;
private MyAdapter mAdapter;
private List<String> mListBak;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView = (ListView)findViewById(R.id.listview);
mBtn = (Button)findViewById(R.id.btn);
mList = new ArrayList<String>();
// mListBak = new ArrayList<String>();
mAdapter = new MyAdapter(mList);
mListView.setAdapter(mAdapter);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
GetDataTask task = new GetDataTask();
task.execute(new Void[0]);
}
});
}
private class GetDataTask extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... params) {
// mListBak.clear();
mList.clear();
for(int i = 0; i < 50; i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// mListBak.add(text+"");
mList.add(text+"");
text++;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// mAdapter.addList(mListBak);
mAdapter.notifyDataSetChanged();
}
}
private class MyAdapter extends BaseAdapter{
private TextView mItem;
private List<String> mData;
public MyAdapter(List<String> data) {
this.mData = data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mData.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
mItem = (TextView)convertView.findViewById(R.id.item);
convertView.setTag(mItem);
}
else{
mItem = (TextView)convertView.getTag();
}
mItem.setText(mData.get(position));
return convertView;
}
public void addList(List list){
mData.addAll(list);
}
}
}
</SPAN>
package com.jackchan.listtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class ListTestActivity extends Activity {
private ListView mListView;
private Button mBtn;
private int text = 0;
private List<String> mList;
private MyAdapter mAdapter;
private List<String> mListBak;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView = (ListView)findViewById(R.id.listview);
mBtn = (Button)findViewById(R.id.btn);
mList = new ArrayList<String>();
// mListBak = new ArrayList<String>();
mAdapter = new MyAdapter(mList);
mListView.setAdapter(mAdapter);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
GetDataTask task = new GetDataTask();
task.execute(new Void[0]);
}
});
}
private class GetDataTask extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... params) {
// mListBak.clear();
mList.clear();
for(int i = 0; i < 50; i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// mListBak.add(text+"");
mList.add(text+"");
text++;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// mAdapter.addList(mListBak);
mAdapter.notifyDataSetChanged();
}
}
private class MyAdapter extends BaseAdapter{
private TextView mItem;
private List<String> mData;
public MyAdapter(List<String> data) {
this.mData = data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mData.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
mItem = (TextView)convertView.findViewById(R.id.item);
convertView.setTag(mItem);
}
else{
mItem = (TextView)convertView.getTag();
}
mItem.setText(mData.get(position));
return convertView;
}
public void addList(List list){
mData.addAll(list);
}
}
}
這段代碼在執行時,如果你在GetDataTask執行doInBackground的時候,去滑動屏幕的ListView,很容易出現
The content of the adapter has changed but ListView did not receive a notification.
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
的系統崩潰錯誤,這段話的意思就是說適配器的內容發生變化,但ListView沒有收到通知,確保適配器的內容只在UI線程更新不要再後台線程更新。
而我們的程序無論是在UI線程還是異步GetDataTask裡都是直接對適配器內容mList進行操作,所以會出現錯誤。
規避以上錯誤的具體方法是:
1、重新分配一個List對象專門在異步任務裡加載數據;
2、在異步任務結束後在UI線程把新分配的List的內容添加到適配器的list裡;
3、在UI線程裡適配器執行notifyDataSetChanged()更新數據
上面的代碼更改如下
[java] view plaincopyprint?
<SPAN style="FONT-SIZE: 18px">package com.jackchan.listtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class ListTestActivity extends Activity {
private ListView mListView;
private Button mBtn;
private int text = 0;
private List<String> mList;
private MyAdapter mAdapter;
private List<String> mListBak;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView = (ListView)findViewById(R.id.listview);
mBtn = (Button)findViewById(R.id.btn);
mList = new ArrayList<String>();
mListBak = new ArrayList<String>();
mAdapter = new MyAdapter(mList);
mListView.setAdapter(mAdapter);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
GetDataTask task = new GetDataTask();
task.execute(new Void[0]);
}
});
}
private class GetDataTask extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... params) {
mListBak.clear();
// mList.clear();
for(int i = 0; i < 50; i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListBak.add(text+"");
// mList.add(text+"");
text++;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mAdapter.addList(mListBak);
mAdapter.notifyDataSetChanged();
}
}
private class MyAdapter extends BaseAdapter{
private TextView mItem;
private List<String> mData;
public MyAdapter(List<String> data) {
this.mData = data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mData.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
mItem = (TextView)convertView.findViewById(R.id.item);
convertView.setTag(mItem);
}
else{
mItem = (TextView)convertView.getTag();
}
mItem.setText(mData.get(position));
return convertView;
}
public void addList(List list){
mData.addAll(list);
}
}
}
</SPAN>
package com.jackchan.listtest;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class ListTestActivity extends Activity {
private ListView mListView;
private Button mBtn;
private int text = 0;
private List<String> mList;
private MyAdapter mAdapter;
private List<String> mListBak;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListView = (ListView)findViewById(R.id.listview);
mBtn = (Button)findViewById(R.id.btn);
mList = new ArrayList<String>();
mListBak = new ArrayList<String>();
mAdapter = new MyAdapter(mList);
mListView.setAdapter(mAdapter);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
GetDataTask task = new GetDataTask();
task.execute(new Void[0]);
}
});
}
private class GetDataTask extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... params) {
mListBak.clear();
// mList.clear();
for(int i = 0; i < 50; i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListBak.add(text+"");
// mList.add(text+"");
text++;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mAdapter.addList(mListBak);
mAdapter.notifyDataSetChanged();
}
}
private class MyAdapter extends BaseAdapter{
private TextView mItem;
private List<String> mData;
public MyAdapter(List<String> data) {
this.mData = data;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mData.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(ListTestActivity.this).inflate(R.layout.item, null);
mItem = (TextView)convertView.findViewById(R.id.item);
convertView.setTag(mItem);
}
else{
mItem = (TextView)convertView.getTag();
}
mItem.setText(mData.get(position));
return convertView;
}
public void addList(List list){
mData.addAll(list);
}
}
}
之前寫過一篇Eclipse制作.so的文章,使用的是GNUstep模擬Linux環境,過程現在看來是想相當麻煩,後來發現一個簡單的方法就是通過項目右鍵添加Native S
首先我們要知道一共有哪幾種動畫,這個面試有可能被問哦^_^。 變換動畫(透明度、縮放、平移、旋轉)、逐幀動畫、布局動畫和屬性動畫一、變換動畫我們可以通過XML文件設置動畫
在開發應用程序的時候,經常會遇到這樣的情況,會在運行時動態根據條件來決定顯示哪個View或某個布局。那麼最通常的想法就是把可能用到的View都寫在上面,先把它們的可見性都
實現功能:歌曲下載完成後通知主界面更新本地音樂除了下面說明的一個問題,還有一些BUG有待修復,後續博文將會繼續更新//DownloadDialogFragment回傳的是