这一篇博客和上一篇讲的都是listView的动态加载,但有所不同的是,本篇的listView是嵌套在ScrollView下的,有时候在一个Activity中可能分为好几个模块,由于展示的需要(手机屏幕大小有限),我们需要在这些模块的外层嵌套ScrollView,这时候我们就不能根据listView的状态来监听是否需要加载数据啦,但是转念一想,我们可以监听scrollview,但scrollview滑动到最底部时,那listview肯定也滑动到底部啦。思路确定啦,但是在开发的过程中发现了一个bug,listView有时候会加载重复的数据,搞的莫名其妙,后来经过查资料得知在ScrollView滑动中,onScrollChanged总是在不停被调用,导致多次向服务器端请求同样的数据,这时候就需要我们自己做并发控制
ZdyListView
1 package com.example.listview; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.widget.ListView; 8 import android.widget.ProgressBar; 9 import android.widget.TextView;10 11 /**12 * Created by keranbin on 2015/10/25.13 */14 public class ZdyListView extends ListView {15 private Context context;16 private View footer;17 private ProgressBar progressBar; 18 private TextView tv;19 20 public ZdyListView(Context context) {21 super(context);22 init(context);23 }24 public ZdyListView(Context context, AttributeSet attrs) {25 super(context, attrs);26 init(context);27 }28 public ZdyListView(Context context, AttributeSet attrs, int defStyle) {29 super(context, attrs, defStyle);30 init(context);31 }32 private void init(Context context) {33 this.context=context;34 footer=LayoutInflater.from(context).inflate(R.layout.activity_footer, null);35 this.addFooterView(footer);36 progressBar=(ProgressBar) footer.findViewById(R.id.progressBar);37 tv=(TextView) footer.findViewById(R.id.tv);38 tv.setText("上拉加载更多");39 }40 41 //正在加载数据,将listview底部提示文字置为"正在加载中。。。。"42 public void onLoading(){43 progressBar.setVisibility(VISIBLE);44 tv.setText("正在加载中。。。。");45 }46 47 //加载完毕,将listView底部提示文字改为"上拉加载更多"48 public void LoadingComplete(){49 progressBar.setVisibility(GONE);50 tv.setText("上拉加载更多");51 }52 53 54 //重写onMeasure,解决scrollview与listview冲突55 @Override56 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {57 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,58 MeasureSpec.AT_MOST);59 super.onMeasure(widthMeasureSpec, expandSpec);60 }61 62 63 64 }
ZdyScrollView
1 package com.example.listview; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.view.View; 6 import android.widget.ScrollView; 7 8 public class ZdyScrollView extends ScrollView{ 9 private int flag=0; //并发控制标志位10 11 private OnZdyScrollViewListener onZdyScrollViewListener;12 13 public ZdyScrollView(Context context) {14 super(context);15 }16 17 public ZdyScrollView(Context context, AttributeSet attrs) {18 super(context, attrs);19 }20 21 public ZdyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {22 super(context, attrs, defStyleAttr);23 }24 25 //listview加载完毕,将并发控制符置为026 public void loadingComponent(){27 flag=0;28 }29 30 @Override31 protected void onScrollChanged(int l, int t, int oldl, int oldt) {32 super.onScrollChanged(l, t, oldl, oldt);33 View view=this.getChildAt(0);34 //如果scrollview滑动到底部并且并发控制符为0,回调接口向服务器端请求数据35 if (this.getHeight() + this.getScrollY() == view.getHeight() && flag == 0) {36 flag = 1;//一进来就将并发控制符置为1,虽然onScrollChanged执行多次,但是由于并发控制符的值为1,不满足条件就不会执行到这37 onZdyScrollViewListener.ZdyScrollViewListener();38 }39 }40 41 public void setOnZdyScrollViewListener(OnZdyScrollViewListener onZdyScrollViewListener){42 this.onZdyScrollViewListener=onZdyScrollViewListener;43 }44 45 public interface OnZdyScrollViewListener{46 public void ZdyScrollViewListener();47 }48 49 }
MainActivity
1 package com.example.listview; 2 3 import java.util.ArrayList; 4 5 import com.example.listview.ZdyScrollView.OnZdyScrollViewListener; 6 7 import android.app.Activity; 8 import android.os.Bundle; 9 import android.os.Handler; 10 import android.os.Message; 11 12 13 public class MainActivity extends Activity { 14 15 private ZdyScrollView scrollView; 16 private ZdyListView listView; 17 private ListViewAdapter adapter; 18 19 //一次从服务器端请求十条数据 20 private int current=1; 21 private int number=9; 22 23 ArrayListlistDatas; 24 25 private Handler handler=new Handler(){ 26 public void handleMessage(Message msg) { 27 setListView((ArrayList )msg.obj); 28 } 29 }; 30 31 32 @Override 33 protected void onCreate(Bundle savedInstanceState) { 34 super.onCreate(savedInstanceState); 35 setContentView(R.layout.activity_main); 36 initView(); 37 initListener(); 38 } 39 40 41 @Override 42 protected void onStart() { 43 super.onStart(); 44 //第一次请求数据 45 getDataThread(current,number); 46 } 47 48 49 50 private void initView() { 51 listView=(ZdyListView) this.findViewById(R.id.listView); 52 scrollView=(ZdyScrollView) this.findViewById(R.id.scrollView); 53 54 } 55 56 private void initListener() { 57 scrollView.setOnZdyScrollViewListener(new OnZdyScrollViewListener() { 58 @Override 59 public void ZdyScrollViewListener() { 60 //上拉加载更多数据 61 getDataThread(current,number); 62 } 63 }); 64 65 } 66 67 68 private void setListView(ArrayList datas) { 69 if(listDatas==null){ //第一次加载数据,为listview设置适配器 70 listDatas=new ArrayList (); 71 listDatas.addAll(datas); 72 adapter=new ListViewAdapter(MainActivity.this,listDatas); 73 listView.setAdapter(adapter); 74 current=adapter.getCount()+1; //记录当前listview中的最后一个数据的下标 75 listView.LoadingComplete(); //告诉listview已经加载完毕,重置提示文字 76 scrollView.loadingComponent();//告示scrollview已经加载完毕,重置并发控制符的值 77 }else{ //下拉加载更多数据,只需要告诉adapter刷新就行 78 listDatas.addAll(datas); 79 adapter.notifyDataSetChanged(); 80 current=adapter.getCount()+1; 81 listView.LoadingComplete(); 82 scrollView.loadingComponent(); 83 } 84 85 }; 86 87 private void getDataThread(final int current, final int number){ 88 final Message msg=new Message(); 89 listView.onLoading(); 90 new Thread(){ 91 public void run() { 92 try { //模拟向服务器请求数据耗时 93 sleep(10000); 94 } catch (InterruptedException e) { 95 e.printStackTrace(); 96 } 97 ArrayList datas=ListViewDatas.returnNum(current, current+number); 98 if(datas!=null&&datas.size()>0){ 99 msg.obj=datas;100 handler.sendMessage(msg);101 }102 };103 }.start();104 }105 106 }
模拟服务器端发回数据ListViewDatas
1 package com.example.listview; 2 3 import java.util.ArrayList; 4 5 public class ListViewDatas { 6 //模拟服务器端数据库中的数据,假设现在只有3条数据 7 public static int NUM=53; 8 public static ArrayListreturnNum(int startNum,int endNum){ 9 ArrayList list=new ArrayList ();10 if(endNum<=NUM){ //客户端请求的数据在数据库数据范围之内11 for(int i=startNum;i<=endNum;i++){12 list.add(String.valueOf(i));13 }14 return list;15 }else if(endNum>=NUM&&startNum<=NUM){ //客户端请求的数据不全在数据库数据范围之内16 for(int i=startNum;i<=NUM;i++){17 list.add(String.valueOf(i));18 }19 return list; 20 }else if(startNum>NUM){ //客户端请求的数据超出数据库数据范围之内21 return null;22 }23 return null;24 }25 }
页面布局activity_main.xml
15 6 10 11 17 1815 16
listView的footView activity_footer.xml
1 26 7 15 16 30 3122 23 28 29