不知道怎麼上傳動圖,下面這張是在動态跳出資料的時候截出來的圖。下面記錄一下,怎麼做出靈活的動态顯示listview資料的效果。
這是在動态顯示的過程中截的一張效果圖,接下來記錄一下如何做出動态顯示listview資料的效果.
涉及到的知識點包括:
1.AyncTask機制
2.synchronize同步代碼塊
關于AsyncTask這裡不做過多講述,不是很了解的童鞋建議先去看一下相關的使用方法。
關于synchronize同步代碼塊的相關知識點(簡單描述):
1.每個java對象都有一個實作同步的鎖,這些鎖稱為内置鎖。線程進入同步代碼塊或方法的時候會自動獲得該鎖,在退出同步代碼塊或方法時會釋放該鎖。獲得内置鎖的唯一途徑就是進入這個鎖的保護的同步代碼塊或方法。
2.鎖分為類鎖和對象鎖:修飾靜态方法時為類鎖,當A線程通路X類的靜态方法時,B線程不能通路X類的任何被synchronize修飾的靜态方法(但是可以通路未被synchronize修飾的靜态方法),隻能等待(處于阻塞狀态)A線程執行完該同步方法後才能執行該方法。修飾普通方法或代碼塊時為對象鎖,當A線程通路X類的一個執行個體對象的靜态方法或代碼塊時,B線程不能通路同一個對象的所有被synchronize修飾的普通方法或代碼塊(但是可以通路未被synchronize修飾的方法或代碼塊),隻能等待A線程執行完該同步方法或同步代碼塊後釋放内置鎖,處于阻塞狀态的B線程才可開始通路。
3.類鎖和對象鎖不是互斥的。當A線程通路X類的同步靜态方法時,B線程可以通路X類對象執行個體的同步方法或代碼塊。
待會我會說明一下為什麼我們會用到synchronize這個知識點,下面我上一段關鍵的代碼片段:
</pre><pre class="java" name="code">public class AsyncAddressBook extends AsyncTask<String, Integer, Object> {
private TextView tv_progress;
private ProgressBar progressBar;
private ContactArrayList contacts;
private AddressBookAdapter contactAdapter;
private SwipeMenuListView swipeMenuListView;
private int action = -1;
private Context context;
public AsyncAddressBook(TextView tv_progress, ProgressBar progressBar, ContactArrayList contacts, AddressBookAdapter contactAdapter, SwipeMenuListView swipeMenuListView) {
this.tv_progress = tv_progress;
this.progressBar = progressBar;
this.contacts = contacts;
this.contactAdapter = contactAdapter;
this.swipeMenuListView = swipeMenuListView;
}
public AsyncAddressBook(Context context,ContactArrayList contacts, AddressBookAdapter contactAdapter, SwipeMenuListView swipeMenuListView) {
this.contacts = contacts;
this.context=context;
this.contactAdapter = contactAdapter;
this.swipeMenuListView = swipeMenuListView;
}
/**
* 0展示全部資料 1展示與編輯框比對的資料
*
* @param action
*/
public void setAction(int action) {
this.action = action;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (progressBar != null && tv_progress != null) {
tv_progress.setVisibility(View.VISIBLE);
tv_progress.setText("0/0");
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
}
}
@Override
protected Object doInBackground(String... params) {
switch (action) {
case 0:
//查詢所有資料
selectAllContacts();
break;
case 1:
//模糊查詢
selectMatchContacts(params[0]);
break;
}
return null;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
contactAdapter.sort();
contactAdapter.notifyDataSetChanged();
if (progressBar != null && tv_progress != null) {
progressBar.setVisibility(View.GONE);
tv_progress.setVisibility(View.GONE);
}
swipeMenuListView.setSelection(0);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (progressBar != null && tv_progress != null) {
progressBar.setProgress(values[0]);
tv_progress.setText(values[0] + "/" + progressBar.getMax());
}
contactAdapter.notifyDataSetChanged();
swipeMenuListView.setSelection(values[0]);
}
/**
* 擷取資料庫所有聯系人
*/
public void selectAllContacts() {
contacts.clear();
SQLiteDatabase sql_read = new DB_Helper(tv_progress.getContext()).getReadableDatabase();
Cursor cursor = sql_read.query(DB_Constants.AddressBook_TableName, DB_Constants.AddressBookColumns, null, null, null, null, null);
if (cursor.getCount() > 0) {
progressBar.setMax(cursor.getCount());
sql_read.beginTransaction();
while (cursor.moveToNext()) {
Contact contact = new Contact();
contact.setContact_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_ID)));
contact.setContact_user_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_User_ID)));
contact.setNick_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Nick_Name)));
contact.setComp_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Comp_Name)));
contact.setUser_image(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Image)));
contact.setArea(cursor.getString(cursor.getColumnIndex(DB_Constants.Area)));
contact.setCreate_time(cursor.getString(cursor.getColumnIndex(DB_Constants.Create_Time)));
contact.setUser_role(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Role)));
contacts.add(contact);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(contacts.size());
}
sql_read.setTransactionSuccessful();
sql_read.endTransaction();
}
cursor.close();
sql_read.close();
}
/**
* 查詢包含S的所有聯系人
*/
public void selectMatchContacts(String s) {
contacts.clear();
SQLiteDatabase sql_read = new DB_Helper(context).getReadableDatabase();
Cursor cursor = sql_read.query(DB_Constants.AddressBook_TableName, DB_Constants.AddressBookColumns, DB_Constants.Nick_Name + " like '%" + s + "%' ", null, null, null, null);
if (cursor.getCount() > 0) {
sql_read.beginTransaction();
while (cursor.moveToNext()) {
Contact contact = new Contact();
contact.setContact_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_ID)));
contact.setContact_user_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_User_ID)));
contact.setNick_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Nick_Name)));
contact.setComp_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Comp_Name)));
contact.setUser_image(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Image)));
contact.setArea(cursor.getString(cursor.getColumnIndex(DB_Constants.Area)));
contact.setCreate_time(cursor.getString(cursor.getColumnIndex(DB_Constants.Create_Time)));
contact.setUser_role(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Role)));
contacts.add(contact);
if (contacts.size() > 0) {
publishProgress(contacts.size());
}
}
sql_read.setTransactionSuccessful();
sql_read.endTransaction();
}
cursor.close();
sql_read.close();
}
}
說一下在測試過程中遇到的問題及解決辦法:
問題:contactAdapter的資料源是contacs,contacts的資料改變(contacts.add(XXX))是在子線程中進行的,而listview資料的重新整理(contactAdapter.notifyDataSetChanged())是在主線程中進行的。當異步任務的背景線程(簡稱A線程)執行完contacts.add(xxx)方法後,立即調用publishProgress(contacts.size());在主線程(簡稱B線程)更新進度條。這是兩個不同的線程,A線程在調用publishProgress(contacts.size())後仍然會往下執行。而B線程現在正在重新整理listview的資料,這個時候肯定會調用contacts.get(position)方法,而因為A線程因為繼續在執行,是以這個時候很有可能A線程正在調用contacts.add(XXX)方法。此時,兩個線程通路的是同一個對象,但是讀到的資料卻可能會不一緻,是以如果上述代碼中的contacts假設是一個普通的ArrayList類型,運作的時候偶爾會出現異常奔潰,奔潰原因是“在另一個線程改變了資料源,卻沒有即時告知adapter在主線程重新整理資料”,産生的原因就是我們在adapter重新整理資料時,資料源的值又變化了。
解決辦法:這個時候可以考慮用同步機制了。這種情況可以用對象鎖。分析可得,可能會産生異常奔潰的原因在于contacts這個資料源的add方法和get方法。是以我們用synchronize修飾add方法和get方法,這樣當線程A通路add方法時,線程B不能再通路這個對象的get方法,隻能等線程A執行完add方法後,線程B才能通路這個對象的get方法。這樣就可以避免兩個線程通路到的資料源不一緻的問題。下面上一段自定義的ArrayList類.
<pre class="java" name="code">public class ContactArrayList extends ArrayList<Contact> {
@Override
public void add(int index, Contact object) {
//對象鎖,this指目前對象
synchronized (this) {
//被上鎖的代碼塊
super.add(index, object);
}
}
@Override
public Contact get(int index) {
synchronized (this) {
return super.get(index);
}
}
}
現在就可以實作靈活的資料重新整理啦!~synchronize要謹慎使用,因為它是阻塞線程的,會消耗性能。
轉載請注明出處!
與 君 共 勉