您的位置:首页 > 其它

[置顶] ListView异步延迟加载

2015-12-04 17:20 155 查看
有些时候ListView的每个item的内容可能比较复杂,需要一系列的操作才能完成,比较耗时,此时应该用异步加载的方式去获取数据,然而仅仅通过这样处理有时并不能达到很好的效果,item很多的时候(比如有几百上千项)一直往下滑动也可能会造成界面卡顿或者OOM,这是因为每滑过一个item时都会试图用异步去请求数据,而数据还没有返回或已返回时已经滑到下面的item中了,这种情况这样做是非常浪费资源的。

我们刷朋友圈或微博的时候细心一点可以发现,加载的时候大概分两步

请求基本的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项目):

点击跳转
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: