ListView中類似於微信朋友圈功能重新整理的使用,listview朋友圈

來源:互聯網
上載者:User

ListView中類似於朋友圈功能重新整理的使用,listview朋友圈

這一篇blog是接著上一篇,上一篇有一些不完美,這裡做出改進。

首先我們需要理清思路:使用ListView顯示資料是很方便的,ListVIew的資料之間通過適配器adapter去作為橋樑串連起來。當我們需要使用listview顯示大量資料的時候,我們需要使用到分頁功能,比如我們有一千條資料,那麼我們應該分開資料一點一點的顯示,比如每次使用者重新整理我就增加20條資料額、展示給使用者,每次都是增加一定量的資料給使用者,直到資料沒有為止。為了改善使用者體驗,我們還應該把上一次使用者退出的時候顯示最新的20條資料保留本地,使用者下次點進來就可以直接看到那些資料,使用者點擊重新整理的時候再去載入本次位於伺服器的最新20資料給使用者,然後把原來的資料覆蓋掉。然後使用者下拉到20條資料底端的時候再去載入那之後的20條資料,這樣往複迴圈就可以實現了。本人這次只是類比。

首先,如何判斷使用者需要載入新的資料,就是需要監聽listview的滾動事件,當使用者滾動到最底屏的時候,有一個可以唯一確定的是,目前使用者螢幕顯示的最後一條資料的position+1等於總的資料量,這時候,我們就應該去添加新的資料,開一條新的線程完成該工作。然後利用handler去把資料添加到適配器的data中,然後調用adapter.notifyDataSetChanged()就可以更新ListVIew了。但是這裡有一些問題需要注意的

1,我們需要保證我們當期使用者點擊下拉重新整理之後下載好的資料全部載入到適配器的data的時候才可以去載入新的資料,這是因為某些使用者可能很急躁,不斷的下拉listview,導致不斷的是的螢幕最後一條資料+1等於總的資料量,不斷去觸發線程下載新資料,所以,我們需要有一個flag去控制。

2,所有的資料下載都需要開線程去完成或者是使用非同步任務,下載圖片的時候建議使用非同步任務,以為當使用者快速滾動的時候會開很多的線程下載圖片,非同步任務能控制線程數量,或者使用Imageloder架構

3,緩衝圖片,每一次我們去載入新的資源的時候我們就需要把最新的20條資料覆蓋原來的,儲存本地,他應該在每一次去請求資料的時候開一條線程去完成。主要注意的是每一次請求的輸入資料流只能使用一次,所以這裡既需要寫入本地有需要寫入記憶體,所以需要請求兩次來獲得兩個資料流,這裡可以看getData()方法。

詳細代碼:

MainActivity

public class MainActivity extends Activity{// 每次都保證把最新的資料儲存到本地,下次使用者點開的時候就可以直接顯示這些以前最新的資料,// 當使用者下拉重新整理之後,在把下拉重新整理之後的資料讀入到該檔案中,每次使用者點開都是上次最新的不過現在沒有重新整理的檔案public static final File saveXMLToSD = new File(Environment.getExternalStorageDirectory(), "list.xml");private ListView listview;// listviewprivate File cache;// 緩衝目錄public static final int OK = 1;// 成功獲得資料public static final int YES = 2;// 成功獲得最新資料資料public static final int ERROR = -1;// 失敗獲得資料private boolean flag = true;private View footer;private ListAdapter adapter;private boolean isFinsh = false;// 負責當資料完成下載之後綁定適配器,使用者首次使用點擊螢幕就不會有異常private Handler mHandler = new Handler(){@SuppressWarnings("unchecked")public void handleMessage(android.os.Message msg){if (msg.what == OK){adapter = new ListAdapter(MainActivity.this, R.layout.list_item, cache, (List<Contacts>) msg.obj);System.out.println("(List<Contacts>) msg.obj" + ((List<Contacts>) msg.obj).size());listview.addFooterView(footer);// 添加頁尾,用於改善使用者體驗listview.setAdapter(adapter);// 綁定適配器listview.removeFooterView(footer);// 首次不用顯式頁尾}if (msg.what == YES)// 成功載入最新資料{flag = true;//這時候才可以繼續去下載資料adapter.setData((List<Contacts>) msg.obj);adapter.notifyDataSetChanged();// 通知數據更改成功,更新ListViewif (listview.getFooterViewsCount() > 0)listview.removeFooterView(footer);// 有頁尾存在則去除,此時已經完全載入資料}if (msg.what == ERROR){Toast.makeText(getApplicationContext(), "網路連接失敗", Toast.LENGTH_SHORT).show();}};};@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listview = (ListView) findViewById(R.id.listview);cache = new File(Environment.getExternalStorageDirectory(), "cahce");if (!cache.exists())cache.mkdirs();footer = getLayoutInflater().inflate(R.layout.footer, null);// 載入頁尾listview.setOnScrollListener(new ListViewScrollListener());// 監聽滾動事件new Thread(new Runnable(){public void run(){try{List<Contacts> data = new ArrayList<Contacts>();if (!saveXMLToSD.exists()){// data.addAll(ContactsService.getData());//// 首次檔案不存在的時候載入資料data.addAll(ContactsService.getData());} else{// 首次資料存在的時候從使用者的xml檔案中讀取資料,這樣給使用者比較快的感覺,每一次登陸都顯示上一次登陸的結果FileInputStream fis = new FileInputStream(saveXMLToSD);// data = ContactsService.parserXML(fis);data.addAll(ContactsService.parserXML(fis));}Message msg = Message.obtain();msg.what = OK;msg.obj = data;mHandler.sendMessage(msg);// 成功發送訊息} catch (Exception e){mHandler.sendEmptyMessage(ERROR);e.printStackTrace();}}}).start();}class ListViewScrollListener implements OnScrollListener{public void onScrollStateChanged(AbsListView view, int scrollState){}/** * firstVisibleItem 螢幕中第一個可見的item的position * visibleItemCount 螢幕中可見的item的數量, * totalItemCount 一共有的資料總量 */public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){int lastItemId = listview.getLastVisiblePosition();//螢幕中最後的一個可視的item的positionSystem.out.println("lastItemId=" + lastItemId + "firstVisibleItem=" + firstVisibleItem + "visibleItemCount=" + visibleItemCount + "totalItemCount=" + totalItemCount);// 當可視的item的最後一條達到了總數目則說明使用者已經達到了資料的最低部,這時候應該從網路擷取最新資料if (lastItemId + 1 == totalItemCount && totalItemCount > 0){if (flag){flag = false;//防止使用者不斷下拉listview,所以一旦有一次下拉到最低端之後資料下載之後,馬上設為false,只有資料下載完成之後才在handlr中設定為true// 這時候把標誌值為true,因為下拉底部的時候需要把這一次的下拉完全擷取玩之前不去再次載入,而擷取是使用線程的,需要時間,所以必須先要把該標誌值為truelistview.addFooterView(footer);// 開啟線程載入最新的資料new Thread(new Runnable(){@Overridepublic void run(){try{Thread.currentThread().sleep(3000);List<Contacts> data = ContactsService.getData();Message msg = Message.obtain();msg.what = YES;msg.obj = data;mHandler.sendMessage(msg);} catch (Exception e){mHandler.sendEmptyMessage(-1);e.printStackTrace();}}}).start();}}}}// 當使用者退出當前應用的時候把所有的緩衝圖片刪除@Overrideprotected void onDestroy(){if (cache.exists()){for (File item : cache.listFiles()){item.delete();}cache.delete();}super.onDestroy();// 一定需要這句,否則會失敗}}
adapter,下載圖片,綁定資料,更新資料

public class ListAdapter extends BaseAdapter{public List<Contacts> data = new ArrayList<Contacts>();private int listItem;private File cache;private LayoutInflater inflater;private Contacts contact;private static ImageView imageview;/** * @return the data */public List<Contacts> getData(){return data;}/** * @param data the data to set */public void setData(List<Contacts> data){this.data.addAll(data);}public ListAdapter (Context mainActivity , int listItem , File cache , List<Contacts> data){this.data.addAll(data);this.listItem = listItem;this.cache = cache;inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);}@Overridepublic int getCount(){return data.size();}@Overridepublic Object getItem(int position){return data.get(position);}@Overridepublic long getItemId(int position){return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){ViewHolder holder = null;if (null == convertView){convertView = inflater.inflate(listItem, null);holder = new ViewHolder();holder.imageview = (ImageView) convertView.findViewById(R.id.imageview);holder.textView = (TextView) convertView.findViewById(R.id.textview);convertView.setTag(holder);} else{holder = (ViewHolder) convertView.getTag();}contact = data.get(position);holder.textView.setText(contact.name);imageview = holder.imageview;loadImageView(contact.path);return convertView;}static class ViewHolder{ImageView imageview;TextView textView;}private void loadImageView(String path){new MyAsyncTask(imageview).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, path);}private class MyAsyncTask extends AsyncTask<String, Void, Uri>{private ImageView imageView;public MyAsyncTask (ImageView imageView){super();this.imageView = imageView;}//後台下載圖片,使用線程池控制,也可以線程對象的重用@Overrideprotected Uri doInBackground(String... params){String path = params[0];try{Uri uri = ContactsService.loadSaveImage(path, cache);return uri;} catch (Exception e){e.printStackTrace();}return null;}@Overrideprotected void onPostExecute(Uri result){if(result!= null && imageView != null)imageView.setImageURI(result);//運行在UI線程直接更新ImageViewelse if(imageView != null){imageView.setImageResource(R.drawable.ic_launcher);//預設圖片}}}}
service,下載資料,緩衝圖片,解析XML

public class ContactsService{/** * 返回最新資料 list集合返回 *  * @return * @throws Exception * @throws IOException */public static List<Contacts> getData() throws Exception, IOException{String pathXML = "http://10.10.117.197:8080/web/list2.xml";HttpClient client = new DefaultHttpClient();HttpPost post = new HttpPost(pathXML);HttpResponse httpResponse = client.execute(post);if (200 == httpResponse.getStatusLine().getStatusCode()){HttpEntity entity = httpResponse.getEntity();final InputStream content = entity.getContent();new Thread(new Runnable(){public void run(){saveToSD(content);// 每一次實現下載最新的資料的同時需要去儲存本地}}).start();return parserXML(client.execute(post).getEntity().getContent());// 返回最新的資料給listview顯示}return null;}/** *  解析XML資料,並以集合返回 * @param content * @return * @throws Exception */public static List<Contacts> parserXML(InputStream content) throws Exception{XmlPullParser parser = Xml.newPullParser();parser.setInput(content, "UTF-8");int event = parser.getEventType();List<Contacts> data = new ArrayList<Contacts>();Contacts item = null;int i = 1;while (event != XmlPullParser.END_DOCUMENT){switch (event){case XmlPullParser.START_TAG:if ("contact".equals(parser.getName())){item = new Contacts();item.id = Integer.valueOf(parser.getAttributeValue(0));break;}if ("name".equals(parser.getName())){item.name = parser.nextText()+i++;break;}if ("image".equals(parser.getName())){item.path = parser.getAttributeValue(0);break;}break;case XmlPullParser.END_TAG:if ("contact".equals(parser.getName())){data.add(item);item = null;}break;}event = parser.next();}return data;}/** * 儲存最新的資料到本地 *  * @param content *            輸入資料流 */private static void saveToSD(InputStream content){try{FileOutputStream fos = new FileOutputStream(MainActivity.saveXMLToSD);int len;byte[] buffer = new byte[1024];while ((len = content.read(buffer)) != -1){fos.write(buffer, 0, len);}fos.close();} catch (FileNotFoundException e){e.printStackTrace();} catch (IOException e){e.printStackTrace();}}/** * 緩衝圖片 *  * @param path *            下載路徑 * @param cache *            緩衝目錄 * @return * @throws Exception */public static Uri loadSaveImage(String path, File cache) throws Exception{File localFile = new File(cache, MD5.getMD5(path) + path.substring(path.lastIndexOf(".")));if (localFile.exists()){return Uri.fromFile(localFile);} else{BufferedOutputStream localFileBufferedOutputStream = null;HttpResponse httpResponse = null;FileOutputStream localFileOutputStream = new FileOutputStream(localFile);localFileBufferedOutputStream = new BufferedOutputStream(localFileOutputStream);HttpClient client = new DefaultHttpClient();HttpPost post = new HttpPost(path);httpResponse = client.execute(post);if (200 == httpResponse.getStatusLine().getStatusCode()){InputStream content = null;try{HttpEntity entity = httpResponse.getEntity();content = entity.getContent();int len;byte[] buffer = new byte[1024];while ((len = content.read(buffer)) != -1){localFileBufferedOutputStream.write(buffer, 0, len);localFileBufferedOutputStream.flush();}return Uri.fromFile(localFile);} catch (IllegalStateException e){e.printStackTrace();} catch (IOException e){e.printStackTrace();} finally{try{localFileBufferedOutputStream.close();} catch (IOException e){e.printStackTrace();}}}}return null;}}
domain

public class Contacts{public int id;public String name;public String path;public Contacts (){}public Contacts (int id , String name , String path){super();this.id = id;this.name = name;this.path = path;}@Overridepublic boolean equals(Object o){return false;}}
Util類 MD5命名

public class MD5 {public static String getMD5(String content) {try {MessageDigest digest = MessageDigest.getInstance("MD5");digest.update(content.getBytes());return getHashString(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return null;}    private static String getHashString(MessageDigest digest) {        StringBuilder builder = new StringBuilder();        for (byte b : digest.digest()) {            builder.append(Integer.toHexString((b >> 4) & 0xf));            builder.append(Integer.toHexString(b & 0xf));        }        return builder.toString();    }}

layout檔案

main

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.example.asynctasklistdemo.MainActivity" >    <ListView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:id="@+id/listview" /></LinearLayout>
listview的item

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center"    android:orientation="horizontal" >    <ImageView        android:id="@+id/imageview"        android:layout_width="100dp"        android:layout_height="100dp"        android:layout_gravity="center"        android:src="@drawable/ic_launcher" />    <TextView        android:id="@+id/textview"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:textColor="#000000"        android:textSize="40sp" /></LinearLayout>

頁尾footer

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:gravity="center"      android:orientation="horizontal">        <ProgressBar          android:layout_width="wrap_content"          android:layout_height="wrap_content"/>      <TextView           android:layout_width="match_parent"          android:layout_height="match_parent"          android:text="資料正在載入。。。"          android:textSize="20sp"          android:layout_gravity="center"          />  </LinearLayout> 

許可權:

    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

結果:








聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.