这一篇博客和上一篇讲的都是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 @Override 56 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加载完毕,将并发控制符置为0 26 public void loadingComponent(){ 27 flag=0; 28 } 29 30 @Override 31 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 ArrayList<String> listDatas; 24 25 private Handler handler=new Handler(){ 26 public void handleMessage(Message msg) { 27 setListView((ArrayList<String>)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<String> datas) { 69 if(listDatas==null){//第一次加载数据,为listview设置适配器 70 listDatas=new ArrayList<String>(); 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<String> 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 ArrayList<String> returnNum(int startNum,int endNum){ 9 ArrayList<String> list=new ArrayList<String>(); 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
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="fill_parent" 3 android:layout_height="fill_parent" 4 android:orientation="vertical" > 5 6 <com.example.listview.ZdyScrollView 7 android:id="@+id/scrollView" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" > 10 11 <com.example.listview.ZdyListView 12 android:id="@+id/listView" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent" > 15 </com.example.listview.ZdyListView> 16 </com.example.listview.ZdyScrollView> 17 18 </LinearLayout>
listView的footView activity_footer.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <LinearLayout 8 android:id="@+id/load_layout" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:gravity="center" 12 android:orientation="horizontal" 13 android:paddingBottom="10dip" 14 android:paddingTop="10dip" > 15 16 <ProgressBar 17 style="?android:attr/progressBarStyleSmall" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:id="@+id/progressBar" 21 android:visibility="gone"/> 22 23 <TextView 24 android:layout_width="wrap_content" 25 android:layout_height="wrap_content" 26 android:text="正在加载..." 27 android:id="@+id/tv"/> 28 29 </LinearLayout> 30 31 </LinearLayout>