Android Scrollview嵌套下listView动态加载数据,解决onScrollChanged执行多次数据重复问题

时间:2022-11-21 15:04:27

这一篇博客和上一篇讲的都是listView的动态加载,但有所不同的是,本篇的listView是嵌套在ScrollView下的,有时候在一个Activity中可能分为好几个模块,由于展示的需要(手机屏幕大小有限),我们需要在这些模块的外层嵌套ScrollView,这时候我们就不能根据listView的状态来监听是否需要加载数据啦,但是转念一想,我们可以监听scrollview,但scrollview滑动到最底部时,那listview肯定也滑动到底部啦。思路确定啦,但是在开发的过程中发现了一个bug,listView有时候会加载重复的数据,搞的莫名其妙,后来经过查资料得知在ScrollView滑动中,onScrollChanged总是在不停被调用,导致多次向服务器端请求同样的数据,这时候就需要我们自己做并发控制

Android Scrollview嵌套下listView动态加载数据,解决onScrollChanged执行多次数据重复问题Android Scrollview嵌套下listView动态加载数据,解决onScrollChanged执行多次数据重复问题

ZdyListView

 package com.example.listview;

 import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; /**
* Created by keranbin on 2015/10/25.
*/
public class ZdyListView extends ListView {
private Context context;
private View footer;
private ProgressBar progressBar;
private TextView tv; public ZdyListView(Context context) {
super(context);
init(context);
}
public ZdyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZdyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
this.context=context;
footer=LayoutInflater.from(context).inflate(R.layout.activity_footer, null);
this.addFooterView(footer);
progressBar=(ProgressBar) footer.findViewById(R.id.progressBar);
tv=(TextView) footer.findViewById(R.id.tv);
tv.setText("上拉加载更多");
} //正在加载数据,将listview底部提示文字置为"正在加载中。。。。"
public void onLoading(){
progressBar.setVisibility(VISIBLE);
tv.setText("正在加载中。。。。");
} //加载完毕,将listView底部提示文字改为"上拉加载更多"
public void LoadingComplete(){
progressBar.setVisibility(GONE);
tv.setText("上拉加载更多");
} //重写onMeasure,解决scrollview与listview冲突
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
} }

ZdyScrollView

 package com.example.listview;

 import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView; public class ZdyScrollView extends ScrollView{
private int flag=0; //并发控制标志位 private OnZdyScrollViewListener onZdyScrollViewListener; public ZdyScrollView(Context context) {
super(context);
} public ZdyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
} public ZdyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} //listview加载完毕,将并发控制符置为0
public void loadingComponent(){
flag=0;
} @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
View view=this.getChildAt(0);
//如果scrollview滑动到底部并且并发控制符为0,回调接口向服务器端请求数据
if (this.getHeight() + this.getScrollY() == view.getHeight() && flag == 0) {
flag = 1;//一进来就将并发控制符置为1,虽然onScrollChanged执行多次,但是由于并发控制符的值为1,不满足条件就不会执行到这
onZdyScrollViewListener.ZdyScrollViewListener();
}
} public void setOnZdyScrollViewListener(OnZdyScrollViewListener onZdyScrollViewListener){
this.onZdyScrollViewListener=onZdyScrollViewListener;
} public interface OnZdyScrollViewListener{
public void ZdyScrollViewListener();
} }

MainActivity

 package com.example.listview;

 import java.util.ArrayList;

 import com.example.listview.ZdyScrollView.OnZdyScrollViewListener;

 import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message; public class MainActivity extends Activity { private ZdyScrollView scrollView;
private ZdyListView listView;
private ListViewAdapter adapter; //一次从服务器端请求十条数据
private int current=1;
private int number=9; ArrayList<String> listDatas; private Handler handler=new Handler(){
public void handleMessage(Message msg) {
setListView((ArrayList<String>)msg.obj);
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
} @Override
protected void onStart() {
super.onStart();
//第一次请求数据
getDataThread(current,number);
} private void initView() {
listView=(ZdyListView) this.findViewById(R.id.listView);
scrollView=(ZdyScrollView) this.findViewById(R.id.scrollView); } private void initListener() {
scrollView.setOnZdyScrollViewListener(new OnZdyScrollViewListener() {
@Override
public void ZdyScrollViewListener() {
//上拉加载更多数据
getDataThread(current,number);
}
}); } private void setListView(ArrayList<String> datas) {
if(listDatas==null){//第一次加载数据,为listview设置适配器
listDatas=new ArrayList<String>();
listDatas.addAll(datas);
adapter=new ListViewAdapter(MainActivity.this,listDatas);
listView.setAdapter(adapter);
current=adapter.getCount()+1; //记录当前listview中的最后一个数据的下标
listView.LoadingComplete(); //告诉listview已经加载完毕,重置提示文字
scrollView.loadingComponent();//告示scrollview已经加载完毕,重置并发控制符的值
}else{//下拉加载更多数据,只需要告诉adapter刷新就行
listDatas.addAll(datas);
adapter.notifyDataSetChanged();
current=adapter.getCount()+1;
listView.LoadingComplete();
scrollView.loadingComponent();
} }; private void getDataThread(final int current, final int number){
final Message msg=new Message();
listView.onLoading();
new Thread(){
public void run() {
try {//模拟向服务器请求数据耗时
sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ArrayList<String> datas=ListViewDatas.returnNum(current, current+number);
if(datas!=null&&datas.size()>0){
msg.obj=datas;
handler.sendMessage(msg);
}
};
}.start();
} }

模拟服务器端发回数据ListViewDatas

 package com.example.listview;

 import java.util.ArrayList;

 public class ListViewDatas {
//模拟服务器端数据库中的数据,假设现在只有3条数据
public static int NUM=53;
public static ArrayList<String> returnNum(int startNum,int endNum){
ArrayList<String> list=new ArrayList<String>();
if(endNum<=NUM){//客户端请求的数据在数据库数据范围之内
for(int i=startNum;i<=endNum;i++){
list.add(String.valueOf(i));
}
return list;
}else if(endNum>=NUM&&startNum<=NUM){//客户端请求的数据不全在数据库数据范围之内
for(int i=startNum;i<=NUM;i++){
list.add(String.valueOf(i));
}
return list;
}else if(startNum>NUM){//客户端请求的数据超出数据库数据范围之内
return null;
}
return null;
}
}

页面布局activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" > <com.example.listview.ZdyScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.example.listview.ZdyListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.listview.ZdyListView>
</com.example.listview.ZdyScrollView> </LinearLayout>

listView的footView  activity_footer.xml

 <?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:orientation="vertical" > <LinearLayout
android:id="@+id/load_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="10dip"
android:paddingTop="10dip" > <ProgressBar
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:visibility="gone"/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载..."
android:id="@+id/tv"/> </LinearLayout> </LinearLayout>