这一篇博客和上一篇讲的都是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>
|