上拉刷新效果是很多app都具备的功能,每次只加载少量的数据,等这些数据全部展示完后,再展示下一组数据,可以很好地避免一次加载过多数据问题,特别是涉及到网络请求时。先看一下效果:
test.gif自定义ListView###
要实现这样的效果,首先定义一个LoadListView
继承自ListView
,实现构造方法
public LoadListView(Context context) {
super(context);
initView(context);
}
public LoadListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public LoadListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
然后创建initView(Context context)
方法,添加一个FooterView
private void initView(Context context) {
footView = LayoutInflater.from(context).inflate(R.layout.foot_view, null);
this.addFooterView(footView);
}
再实现AbsListView.OnScrollListener
接口,为LoadListView
添加滚动事件监听,实现该接口需要实现以下两个方法:
-
public void onScrollStateChanged(AbsListView view, int scrollState)
scrollState
有三种状态,分别是SCROLL_STATE_IDLE
、SCROLL_STATE_TOUCH_SCROLL
、SCROLL_STATE_FLING
SCROLL_STATE_IDLE
是当屏幕停止滚动时
SCROLL_STATE_TOUCH_SCROLL
是当用户在以触屏方式滚动屏幕并且手指仍然还在屏幕上时(The user is scrolling using touch, and their finger is still on the screen)
SCROLL_STATE_FLING
是当用户由于之前划动屏幕并抬起手指,屏幕产生惯性滑动时(The user had previously been scrolling using touch and had performed a fling) -
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
firstVisibleItem
表示在当前屏幕显示的第一个listItem在整个listView里面的位置(下标从0开始) **
visibleItemCount
表示在现时屏幕可以见到的ListItem(部分显示的ListItem也算)**总数
totalItemCount
表示ListView的ListItem总数 (包括FooterView和HeaderView的数目)
比如下面的图:
这是打印的日志信息:
- firstVisible是第一个可见元素下标,这里是第2个(虽然被挡住了)
- visibleItem是当前可见的数目,从2到10一共9个
- totalItem是总共有46条数据(包括一个FooterView)
- lastItem是自己定义的变量,表示最后一个可见元素下标
- adapterItemCount自己定义的变量,表示总共45条数据(不包括FooterView)
下面实现该接口,首先在initView
方法中注册监听
private void initView(Context context) {
......
this.setOnScrollListener(this);
}
然后让LoadListView
实现AbsListView.OnScrollListener
public class LoadListView extends ListView implements AbsListView.OnScrollListener
接着实现相应的方法:
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(mLastItemCount == mAdapterItemCount
&& scrollState == NumberPicker.OnScrollListener.SCROLL_STATE_IDLE){
//加载数据
if(!isLoading) {
isLoading = true;
mLoadListener.onLoad();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mAdapterItemCount = totalItemCount - 1;
mLastItemCount = firstVisibleItem + visibleItemCount - 1;
if(mLastItemCount < mAdapterItemCount){
isLoading = false;
}
}
当最后一个可见元素等于数据条数的时候,换句话说,当footerview可见时,并且滑动状态处于滑动停止,那么向外提供一个接口,实现加载数据的操作,为了一次只加载一页数据,所以需要用isLoading
来限制只加载一次。不然会出现,在加载数据时不断滑动加载多次数据的情况。当加载数据完成后,一般会调用ListView的adapter的adapter.notifyDataSetChanged()
方法,这时,mAdapterItemCount
即数据条数会增加,导致mLastItemCount < mAdapterItemCount
,从而将isLoading
状态置为false
,准许下一次加载数据。
MainActivity中onLoad()
方法的实现:
handler.postDelayed(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++) {
mItemList.add(new ItemBean("item" + count , R.drawable.icon2));
count++;
}
adapter.notifyDataSetChanged();
}
},3000);
这里一般是网络请求下一页的数据,这里直接用延时程序代替了。
所有源码###
MainActivity
public class MainActivity extends AppCompatActivity implements LoadListView.LoadListener {
private List<ItemBean> mItemList;
private LoadListView listView;
private MyAdapter adapter;
int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (LoadListView) findViewById(R.id.list_view);
mItemList = new ArrayList<>();
for(int i=0;i<20;i++) {
mItemList.add(new ItemBean("item" + count , R.drawable.icon1));
count++;
}
adapter = new MyAdapter();
listView.setAdapter(adapter);
listView.setLoadListener(this);
}
private Handler handler = new Handler();
@Override
public void onLoad() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++) {
mItemList.add(new ItemBean("item" + count , R.drawable.icon2));
count++;
}
adapter.notifyDataSetChanged();
}
},3000);
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
// Log.d("mytag",mItemList.size()+"");
return mItemList.size();
}
@Override
public Object getItem(int position) {
// Log.d("mytag", mItemList.get(position).toString());
return mItemList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, null);
holder.iv = (ImageView) convertView.findViewById(R.id.iv);
holder.tv = (TextView) convertView.findViewById(R.id.tv);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ItemBean bean = mItemList.get(position);
Log.d("mytag", bean.toString());
holder.tv.setText(bean.getText());
holder.iv.setBackgroundResource(bean.getImgId());
return convertView;
}
class ViewHolder {
ImageView iv;
TextView tv;
}
}
}
LoadListView
public class LoadListView extends ListView implements AbsListView.OnScrollListener {
private View footView;
private int mLastItemCount;
private int mAdapterItemCount;
private boolean isLoading = false;
public LoadListView(Context context) {
super(context);
initView(context);
}
public LoadListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public LoadListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {
footView = LayoutInflater.from(context).inflate(R.layout.foot_view, null);
this.addFooterView(footView);
this.setOnScrollListener(this);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(mLastItemCount == mAdapterItemCount
&& scrollState == NumberPicker.OnScrollListener.SCROLL_STATE_IDLE){
//加载数据
if(!isLoading) {
isLoading = true;
if(mLoadListener != null) {
mLoadListener.onLoad();
}
Log.d(TAG, "onScrollStateChanged: " + "onLoad()");
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//计算数据条数,减去
mAdapterItemCount = totalItemCount - 1;
mLastItemCount = firstVisibleItem + visibleItemCount - 1;
if(mLastItemCount < mAdapterItemCount){
isLoading = false;
}
}
private LoadListener mLoadListener;
public void setLoadListener(LoadListener loadListner){
this.mLoadListener = loadListner;
}
public void setAdapterCount(int count) {
this.mAdapterItemCount = count;
}
public void setIsLoading(boolean isLoading) {
this.isLoading = isLoading;
}
public interface LoadListener{
void onLoad();
}
}
ItemBean
public class ItemBean {
private String text;
private int imgId;
public void setText(String text) {
this.text = text;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
public String getText() {
return text;
}
public int getImgId() {
return imgId;
}
public ItemBean(String text, int imgId) {
this.text = text;
this.imgId = imgId;
}
@Override
public String toString() {
return "ItemBean{" +
"text='" + text + '\'' +
", imgId=" + imgId +
'}';
}
}
网友评论