[置顶] ListView异步延迟加载
2015-12-04 17:20
155 查看
有些时候ListView的每个item的内容可能比较复杂,需要一系列的操作才能完成,比较耗时,此时应该用异步加载的方式去获取数据,然而仅仅通过这样处理有时并不能达到很好的效果,item很多的时候(比如有几百上千项)一直往下滑动也可能会造成界面卡顿或者OOM,这是因为每滑过一个item时都会试图用异步去请求数据,而数据还没有返回或已返回时已经滑到下面的item中了,这种情况这样做是非常浪费资源的。
我们刷朋友圈或微博的时候细心一点可以发现,加载的时候大概分两步
请求基本的json数据,生成ListView
在滑动停下来的时候才去加载图片等数据
不多扯了,直接说实现部分:
封装一个LazyListView继承自ListView
编写一个异步类模拟后台的数据请求:
编写一个Adapter ,注意这里的getView只加载基本的占位数据,然后开放一个方法用来获取所有的view:
最后就是在Activity中的listView变量设置一下对懒加载的监听就好了:
运行效果图:
项目下载地址(androidStudio项目):
点击跳转
我们刷朋友圈或微博的时候细心一点可以发现,加载的时候大概分两步
请求基本的json数据,生成ListView
在滑动停下来的时候才去加载图片等数据
不多扯了,直接说实现部分:
封装一个LazyListView继承自ListView
package com.turbo.listviewtest; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.widget.AbsListView; import android.widget.ListView; import java.util.ArrayList; import java.util.List; /** * 懒加载ListView(只在ListView滑动截止时才加载复杂数据) * Created by Turbo on 2015/12/4. */ public class LazyListView extends ListView implements AbsListView.OnScrollListener{ private static final String TAG = "LazyListView"; private OnScrollListener onScrollListener; private int oldVisibleItemCount=0; private OnLazyLoadListener onLazyLoadListener; //记录Item的懒加载情况 //比如(1,true)表示为position为1的item已经懒加载过了 //(2,false) 表示postion为2的item还没有被懒加载 private 4000 SparseArray<Boolean> itemsNow; public LazyListView(Context context) { super(context); init(); } public LazyListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LazyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { //调用父类的设置监听方法监听滑动事件 super.setOnScrollListener(this); itemsNow=new SparseArray<>(); } /** * 覆盖setOnScrollListener方法截取回调 * @param l */ @Override public void setOnScrollListener(OnScrollListener l) { this.onScrollListener=l; } /** * 当ListView出现滑动时会回调这个方法 * 但ListView首次显示时并没有滑动,所以还需要通过onScroll方法判断一下ListView的首次展示 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //Log.d(TAG, "onScrollStateChanged->scrollState=" + scrollState); updateShouldLoadPos(); if (scrollState == 0 ) { caculateWhichShouldLoad(this.getFirstVisiblePosition(), this.getLastVisiblePosition()); } //将回调传递下去,这样不影响在外部调用监听方法 if (this.onScrollListener != null) { this.onScrollListener.onScrollStateChanged(view, scrollState); } } /** * 当ListView首次加载时,这个方法会调用多次,同时这个方法第一次被 * 调用时visibleItemCount的值为0,所以可以跟据这个特性判断listView是不是首次显示 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //Log.d(TAG, "onScroll->firstVisibleItem=" + firstVisibleItem + ",visibleItemCount=" + visibleItemCount + ",totalItemCount=" + totalItemCount); //判断是不是首次显示 if (visibleItemCount != oldVisibleItemCount && oldVisibleItemCount != -1) { updateShouldLoadPos(); caculateWhichShouldLoad(firstVisibleItem, firstVisibleItem + visibleItemCount - 1); oldVisibleItemCount = -1; } if (oldVisibleItemCount != -1) { oldVisibleItemCount = visibleItemCount; } //将回调传递下去,这样不影响在外部调用监听方法 if (this.onScrollListener != null) { this.onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } /** * 更新需要重新加载的项 * 将不再显示范围内的项设为未加载 */ private void updateShouldLoadPos(){ int firstPos=this.getFirstVisiblePosition(); int finalPos=this.getLastVisiblePosition(); for (int i = 0; i < firstPos; i++) { itemsNow.put(i,false); } for(int i=finalPos+1;i<this.getCount();i++){ itemsNow.put(i,false); } } private void caculateWhichShouldLoad(int firstPos,int finalPos) { List<Integer> itemsPos = new ArrayList<>(); for(int i=firstPos;i<=finalPos;i++) { if(!itemsNow.get(i, false)) { itemsPos.add(i); itemsNow.put(i,true); } } Log.d(TAG, "需要加载的position:" + itemsPos.toString()); if (onLazyLoadListener != null) { onLazyLoadListener.shouldLoad(itemsPos); } } /** * 懒加载回调接口 * 在ListView滑动停止后回掉 */ public interface OnLazyLoadListener{ /** * 应该被加载细节的项 * @param itemsPos item的位置集合 */ void shouldLoad(List<Integer> itemsPos); } public void setOnLazyLoadListener(OnLazyLoadListener onLazyLoadListener) { this.onLazyLoadListener = onLazyLoadListener; } }
编写一个异步类模拟后台的数据请求:
package com.turbo.listviewtest; import android.os.AsyncTask; import android.widget.TextView; import java.lang.ref.WeakReference; /** * 模拟复杂的请求操作 * Created by Turbo on 2015/12/4. */ public class BackGroundRequest extends AsyncTask<Integer,String,String> { private static final String TAG = "BackGroundRequest"; private int position; private WeakReference textViewWeakReference=null; public BackGroundRequest(TextView textView) { textViewWeakReference = new WeakReference(textView); } @Override protected String doInBackground(Integer... params) { position=params[0]; try { publishProgress(position + "开始加载..."); Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } return position + "加载完成!!!"; } @Override protected void onProgressUpdate(String... values) { if (textViewWeakReference != null) { ((TextView) textViewWeakReference.get()).setText(values[0]); } } @Override protected void onPostExecute(String s) { if (textViewWeakReference != null) { ((TextView) textViewWeakReference.get()).setBackgroundResource(R.mipmap.ic_launcher); ((TextView) textViewWeakReference.get()).setText(s); } } }
编写一个Adapter ,注意这里的getView只加载基本的占位数据,然后开放一个方法用来获取所有的view:
package com.turbo.listviewtest; import android.content.Context; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * Created by Turbo on 2015/12/4. */ public class ListViewAdapter extends BaseAdapter { private static final int ITEM_COUNT=300; private WeakReference weakContext=null; //将所有的view用弱引用缓存起来 private List<WeakReference<View>> views=null; public ListViewAdapter(Context context) { this.weakContext = new WeakReference(context); views = new ArrayList<>(ITEM_COUNT); for (int i = 0; i < ITEM_COUNT; i++) { views.add(null); } } @Override public int getCount() { return 300; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { //这里只初始化基本的占位布局 TextView textView=null; if (convertView == null) { textView = new TextView((Context)weakContext.get()); textView.setGravity(Gravity.BOTTOM | Gravity.CENTER); textView.setWidth(400); textView.setHeight(200); convertView=textView; } else { textView=(TextView)convertView; textView.setBackgroundResource(0); } textView.setTag(position); textView.setText(position + ":正在准备加载"); views.set(position, new WeakReference<View>(textView)); return textView; } public List<WeakReference<View>> getBaseView(){ return views; } }
最后就是在Activity中的listView变量设置一下对懒加载的监听就好了:
package com.turbo.listviewtest; import android.app.Activity; import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.util.SparseArray; import andro a39a id.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; public class MainActivity extends Activity { private LazyListView listView; private List<WeakReference<View>> views; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListViewAdapter listViewAdapter=new ListViewAdapter(this); views=listViewAdapter.getBaseView(); listView = (LazyListView) findViewById(R.id.lv_Data); listView.setAdapter(listViewAdapter); listView.setOnLazyLoadListener(new LazyListView.OnLazyLoadListener() { @Override public void shouldLoad(List<Integer> itemsPos) { for (Integer i : itemsPos) { new BackGroundRequest((TextView)views.get(i).get()).execute(i); } } }); } }
运行效果图:
项目下载地址(androidStudio项目):
点击跳转
相关文章推荐
- NSTimer 注意事项
- Matlab对路径的操作
- 解决Maven关于本地jar包的打包处理
- Eclipse安装birt
- 使用系统的CoreLocation定位
- 安卓模拟器按键
- Android 高级编程 RecyclerView 控件的使用
- SSH无密码登录
- git学习笔记——本地
- Git分支的使用
- 1038. Recover the Smallest Number (30)【排序】——PAT (Advanced Level) Practise
- Derby-10.11学习笔记(三)Derby系统表及存储过程
- 排雷记录:Swift +Objective-C混合Framework的一些问题
- Maven构建简单的多模块项目
- XSS防御篇
- SkylineGlobe 如何二次开发获取三维模型的BBOX和设置Tint属性
- HDU 1078 FatMouse and Cheese 记忆化搜索DP
- button设置边宽和圆角
- HDU - 1879 继续畅通工程(最小生成树)
- 数据库oracle 11g 监控管理