Android Scrollview嵌套下listView動態加載數據,解決onScrollChanged執行多次數據重復問題


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM