基于Android官方AsyncListUtil优化经典ListView分页加载机制(二)

我写的附录文章1,介绍了如何使用Android官方的分页加载框架AsyncListUtil优化改进常见的RecyclerView分页加载实现。AsyncListUtil作为一种通用的分页加载框架,不仅可以套用在RecyclerView,也可也适用在经典(传统)ListView中,下面给出一个简单例子,说明如何通过AsyncListUtil调整ListView的分页加载机制。
一个简单的MainActivity适用AsyncListUtil和ListView,展示分页加载:

package zhangphil.app; 
 
import android.app.ListActivity; 
import android.content.Context; 
import android.graphics.Color; 
import android.os.Bundle; 
import android.os.SystemClock; 
import android.support.annotation.NonNull; 
import android.support.annotation.Nullable; 
import android.support.v7.util.AsyncListUtil; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.AbsListView; 
import android.widget.ArrayAdapter; 
import android.widget.ListView; 
import android.widget.TextView; 
 
public class MainActivity extends ListActivity { 
    private final String TAG = "调试"; 
    private final int NULL = -1; 
    private AsyncListUtil<DataItem> mAsyncListUtil; 
    private MyAdapter mAdapter; 
    private int mFirstVisibleItem = NULL, mVisibleItemCount = NULL; 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
 
        MyDataCallback mDataCallback = new MyDataCallback(); 
        MyViewCallback mViewCallback = new MyViewCallback(); 
        mAsyncListUtil = new AsyncListUtil(DataItem.class, 20, mDataCallback, mViewCallback); 
 
        mAdapter = new MyAdapter(this, NULL); 
        setListAdapter(mAdapter); 
 
        getListView().setOnScrollListener(new AbsListView.OnScrollListener() { 
            @Override 
            public void onScrollStateChanged(AbsListView absListView, int scrollState) { 
                mAsyncListUtil.onRangeChanged(); 
            } 
 
            @Override 
            public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
                mFirstVisibleItem = firstVisibleItem; 
                mVisibleItemCount = visibleItemCount; 
            } 
        }); 
    } 
 
    @Override 
    protected void onListItemClick(ListView l, View v, int position, long id) { 
        mAsyncListUtil.refresh(); 
    } 
 
    private class MyDataCallback extends AsyncListUtil.DataCallback<DataItem> { 
 
        @Override 
        public int refreshData() { 
            //更新数据的个数。 
            //假设预先设定更新若干条。 
            return Integer.MAX_VALUE; 
        } 
 
        /** 
         * 在这里完成数据加载的耗时任务。 
         * 
         * @param data 
         * @param startPosition 
         * @param itemCount 
         */ 
        @Override 
        public void fillData(DataItem[] data, int startPosition, int itemCount) { 
            Log.d(TAG, "fillData:" + startPosition + "," + itemCount); 
 
            for (int i = 0; i < itemCount; i++) { 
                DataItem dataItem = new DataItem(); 
                dataItem.pos = startPosition + i; 
                dataItem.content = String.valueOf(System.currentTimeMillis()); 
 
                data[i] = dataItem; 
 
                //模拟耗时任务,故意休眠一定时延。 
                SystemClock.sleep(100); 
            } 
        } 
    } 
 
    private class MyViewCallback extends AsyncListUtil.ViewCallback { 
 
        /** 
         * @param outRange 
         */ 
        @Override 
        public void getItemRangeInto(int[] outRange) { 
            outRange[0] = mFirstVisibleItem; 
            outRange[1] = mFirstVisibleItem + mVisibleItemCount; 
 
            /** 
             * 如果当前ListView为空,主动为用户加载数据. 
             * 假设预先加载若干条数据 
             * 
             */ 
            if (outRange[0] == NULL && outRange[1] == NULL) { 
                Log.d(TAG, "当前ListView为空!"); 
                outRange[0] = 0; 
                outRange[1] = 9; 
            } 
 
            Log.d(TAG, "getItemRangeInto,当前可见position: " + outRange[0] + " ~ " + outRange[1]); 
        } 
 
        @Override 
        public void onDataRefresh() { 
            mAdapter.notifyDataSetChanged(); 
            Log.d(TAG, "onDataRefresh"); 
        } 
 
        @Override 
        public void onItemLoaded(int position) { 
            mAdapter.notifyDataSetChanged(); 
            Log.d(TAG, "onItemLoaded:" + position); 
        } 
    } 
 
    private class MyAdapter extends ArrayAdapter { 
        private ViewHolder holder; 
 
        public MyAdapter(@NonNull Context context, int resource) { 
            super(context, resource); 
        } 
 
        @NonNull 
        @Override 
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 
            if (convertView == null) { 
                convertView = LayoutInflater.from(getApplicationContext()).inflate(android.R.layout.simple_list_item_2, null); 
                holder = new ViewHolder(convertView); 
                convertView.setTag(holder); 
            } else { 
                holder = (ViewHolder) convertView.getTag(); 
            } 
 
            holder.setData(getItem(position)); 
 
            return convertView; 
        } 
 
        @Override 
        public int getCount() { 
            return mAsyncListUtil.getItemCount(); 
        } 
 
        @Nullable 
        @Override 
        public DataItem getItem(int position) { 
            return mAsyncListUtil.getItem(position); 
        } 
    } 
 
    private class ViewHolder { 
        public TextView text1; 
        public TextView text2; 
 
        public ViewHolder(View view) { 
            text1 = view.findViewById(android.R.id.text1); 
            text1.setTextColor(Color.RED); 
            text2 = view.findViewById(android.R.id.text2); 
            text2.setTextColor(Color.BLUE); 
        } 
 
        public void setData(DataItem dataItem) { 
            if (dataItem == null) { 
                text1.setText("pos加载中..."); 
                text2.setText("content加载中..."); 
            } else { 
                text1.setText(String.valueOf(dataItem.pos)); 
                text2.setText(dataItem.content); 
            } 
        } 
    } 
 
    private class DataItem { 
        public int pos; 
        public String content; 
    } 
}

(一)和RecyclerView一样,我在ListView中同样适用ListView的滚动事件触发AsyncListUtil的onRangeChanged,从而触发分页加载机制开始运作。
(二)作为演示,本例在ListView的普通item点击事件触发一次AsyncListUtil的refresh事件,这种情况模拟当用户长期停留在一个页面需要为用户主动刷新数据的开发场景。
(三)一点儿特别注意:和附录文章1在RecyclerView中的AsyncListUtil.DataCallback不同,在RecyclerView中的DataCallback,加载数据fillData方法的第一个参数data数组,如果作为基本数据类型如String,int等等这类,直接data[i]是没有问题的,data[i]已经被AsyncListUtil创建和初始化完成,但是如果自定义数据类型,比如本例自定义了DataItem,意图装载一些开发者自定义的复杂类型,此时直接取出的data[i]为null!所以,为了解决这个问题,要从两方面入手:
(a)一方面,如果是自定义的数据结构,在fillData中,每一个data[i]为其重新创建new出来一个对象实例,然后赋值给data[i],本例是data[i]=new DataItem()。
(b)另一方面,在最后一关对View进行赋值时候,判断自定义类型是否为null,不会空指针时候才取出自定义数据结构中的数据元素(本例是DataItem中的成员变量)使用。
附录:
1,《基于Android官方AsyncListUtil优化改进RecyclerView分页加载机制(一)》链接:http://blog.csdn.net/zhangphil/article/details/78603499 
2,《基于Android官方Paging Library的RecyclerView分页加载框架》链接:http://blog.csdn.net/zhangphil/article/details/78627332 

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

Android Activity的launchMode四种启动模式备忘详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。