繼承SwipeRefreshLayout實現上拉刷新

這裏寫圖片描述

來自掘金請點擊
對了,我就通過這篇文章寫出這個上拉刷新的,非常感謝他,但是我還是要吐槽一下,因爲他裏面有個問題並沒有提及怎麼解決,addFooterView()在setAdapter()後調用無法顯示的問題,後面我自己也有寫錯一個,出現問題,文末我會提示注意事項,下面請看我如何寫。

基本邏輯就是:
觸摸滾動時,滾動到最後一條數據時,顯示底部加載條,並加載數據,數據加載完成隱藏底部加載條

一開始當然是要獲取自定義佈局所嵌套的子控件,即嵌套的listView嘛,因爲要需要addFooterView()嘛

if (listView==null) {//這裏listview是一個全局變量
            if (getChildCount()>0) {
                for (int i = 0; i < getChildCount(); i++) {
                    if (getChildAt(i) instanceof ListView) {
                        listView=(ListView) getChildAt(i);
                        Log.i(CustomSwipeRefreshLayout, "找到了LIstView");
                        initLoadLayout();//初始化加載控件
                        setListViewOnScroll();//滾動監聽
                        break;
                    }else {
                        Log.i(CustomSwipeRefreshLayout, "不是LIstView的實例");
                    }
                }
                Log.i(CustomSwipeRefreshLayout, "LIstView是否爲空:"+(listView==null));
            }
        }

底部刷新的View,我是自己用java寫,這樣有個好處,下次要用直接一個類拷走,代碼看起來好像有點多,其實很簡單

    /**
     * 初始化底部加載視圖
     */
    private void initLoadLayout() {
        //佈局,由於父控件是ListView,所以 LayoutParams 是AbsListView的LayoutParams
        AbsListView.LayoutParams listLayoutParams =new AbsListView.LayoutParams(listView.getLayoutParams()); 
        listLayoutParams.width=LayoutParams.MATCH_PARENT;
        listLayoutParams.height=100;
        loadLayout=new LinearLayout(context);//這裏是一個全局變量哦,初始化這個,其他地方就可以用了
        loadLayout.setOrientation(LinearLayout.HORIZONTAL);
        loadLayout.setLayoutParams(listLayoutParams);
        loadLayout.setGravity(Gravity.CENTER_HORIZONTAL);
        //dialog
        android.view.ViewGroup.LayoutParams layoutParams =new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT);
        ProgressBar progressBar=new ProgressBar(context,null,android.R.attr.progressBarStyleInverse);
        progressBar.setLayoutParams(layoutParams);
        //textview
        android.view.ViewGroup.LayoutParams layoutParams2 =new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT);
        TextView textView=new TextView(context);
        textView.setText("正在加載.....");
        textView.setTextSize(15);
        textView.setLayoutParams(layoutParams2);
        textView.setGravity(Gravity.CENTER_VERTICAL);
        //設置子控件
        loadLayout.addView(progressBar);
        loadLayout.addView(textView);
    }

基本上靠這段代碼來判斷是否加載的

/**
     * 設置滾動監聽
     */
    private void setListViewOnScroll() {
        if (listView!=null) {
            listView.setOnScrollListener(new OnScrollListener() {
                //正在移動
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    Log.i(CustomSwipeRefreshLayout, ""+listView.getLastVisiblePosition());
                    if (canLoadMore()) {//判斷加載條件是否成立
                        loadData();//加載數據
                    }else {
                        Log.i(CustomSwipeRefreshLayout, "不可以加載新數據");    
                    }
                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    // TODO Auto-generated method stub

                }
            });
        }
    }

我們需要先把所有條件成立,即什麼時候可以加載數據的條件:
1、是否已經正在加載數據了,正在加載,我們不允許再次加載的,因爲一般都執行線程,所以執行太多會卡的
2、是否滑動到最後一個item,所以使用listview.getLastVisblePosition()==(listview.getCount()-1)
3、觸摸滑動的距離是否符合我們的標準的

獲取觸摸,用來判斷是否符合滑動距離

    //獲取startY和endY
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //按下時
        if (ev.getAction()==MotionEvent.ACTION_DOWN) {
            startY=ev.getY();
        }
        //手指離開時
        else if(ev.getAction()==MotionEvent.ACTION_UP){
            endY=ev.getY();
        }
        return super.dispatchTouchEvent(ev);
    }

這個就是加載條件的代碼

/**
     * 三個條件可以加載數據
     * 1、滑動距離合適的時候
     * 2、最後一個條目
     * 3、沒有正在加載數據
     * @return
     */
    protected boolean canLoadMore() {
        //判斷沒有在加載
        boolean condition1=false;
        if (!isLoading){
            condition1=true;
        }
        //判斷是最後item並且已顯示
        boolean condition2=false;
        if (listView.getLastVisiblePosition()==(listView.getCount()-1)) {
            condition2=true;
        }
        //判斷滑動距離是否合適,touchInstance這個設置個差不多就行
        boolean condition3=false;
        if ((startY-endY)>touchInstance) {
            condition3=true;    
        }
        Log.i(CustomSwipeRefreshLayout, "是否正在加載"+condition1+"是否是最後一個並且已經顯示出來"+condition2+"觸摸距離是否合適"+condition3);        
        return condition1&&condition2&&condition3;
    }

所有條件成立之後我們就可以加載數據了

/**
     * 接口回調實現自定義加載數據
     */
    protected void loadData() {

        if (onLoadListener!=null) {
            if (loadLayout!=null) {
                addLoadLayout();//添加footerView
            }
            onLoadListener.onLoad();
        }

    }
    //調用這個我們自定義刷新控件
        public void setOnLoadListener(OnLoadListener onLoadListener) {
        this.onLoadListener = onLoadListener;
    }

那什麼時候remove加載條呢

//外部調用,即用戶重寫onLoadListener在onload方法中調用即可
    public void setOnload(boolean isLoad){
        isLoading=isLoad;
        if (!isLoad) {
            removeLoadLayout();
        }
    }

以下使用方法
XML寫法

<com.tc.customswiperefreshview.CustomSwipeRefreshLayout 
    android:id="@+id/customSwipeRefreshLayout "
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tc.customswiperefreshview.MainActivity" >
    <ListView 
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    </ListView>
</com.tc.customswiperefreshview.CustomSwipeRefreshLayout>

activity調用

customSwipeRefreshLayout.setOnLoadListener(new CustomSwipeRefreshLayout.OnLoadListener() {

            @Override
            public void onLoad() {
                new Handler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        if (test) {
                            for (int i = 15; i <20; i++) {
                                data.add("我是天才"+i);
                                test=false;
                            }
                        }else {
                            Toast.makeText(MainActivity.this, "沒有數據啦", Toast.LENGTH_SHORT).show();
                        }
                        customSwipeRefreshLayout.setOnload(false);
                        adapter.notifyDataSetChanged();
                    }
                }, 1000);
            }
        });

接下來是兩點注意問題:
1、addFooterView()需要在setAdapter之前調用怎麼解決呢,其實我的這個解決也是不怎麼好,對,就是重新獲取adapter重新適配

private void addLoadLayout() {
        listView.addFooterView(loadLayout);
        if ( listView.getAdapter() instanceof BaseAdapter) {
            BaseAdapter adapter=(BaseAdapter) listView.getAdapter() ;
            listView.setAdapter(adapter);
            Log.i(CustomSwipeRefreshLayout, "是baseAdapter");
        }else{
            Log.i(CustomSwipeRefreshLayout, "不是baseAdapter");
        }
    }

2、之前一直報這個錯誤
Caused by: java.lang.NoSuchMethodException: [class android.content.Context, interface android.util.AttributeSet]
出錯原因
1)

public CustomSwipeRefreshLayout(Context context) {
        super(context);
    }
    public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {//沒有生成這個構造方法,所以報錯
        super(context, attrs);
        this.context=context;
    }

2)

    private CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
    }

不知道有沒有發現什麼不一樣的,就訪問權限,private,說起來也是奇葩,eclipse快捷鍵自動生成的,居然是private

接下來貼出全部代碼

package com.tc.customswiperefreshview;

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
    private static final String CustomSwipeRefreshLayout = "CustomSwipeRefreshLayout";
    public OnLoadListener onLoadListener;
    Context context;
    ListView listView;
    float startY=0;
    float endY=0;
    private static  float touchInstance=150;
    boolean isLoading=false;
    LinearLayout loadLayout;
    public CustomSwipeRefreshLayout(Context context) {
        super(context);
    }
    public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        if (listView==null) {
            if (getChildCount()>0) {
                for (int i = 0; i < getChildCount(); i++) {
                    if (getChildAt(i) instanceof ListView) {
                        listView=(ListView) getChildAt(i);
                        Log.i(CustomSwipeRefreshLayout, "找到了LIstView");
                        initLoadLayout();//初始化加載控件
                        setListViewOnScroll();//滾動監聽
                        break;
                    }else {
                        Log.i(CustomSwipeRefreshLayout, "不是LIstView的實例");
                    }
                }
                Log.i(CustomSwipeRefreshLayout, "LIstView是否爲空:"+(listView==null));
            }
        }
        super.onLayout(changed, left, top, right, bottom);
    }



    public OnLoadListener getOnLoadListener() {
        return onLoadListener;
    }

    public void setOnLoadListener(OnLoadListener onLoadListener) {
        this.onLoadListener = onLoadListener;
    }

    /**
     * 設置滾動監聽
     */
    private void setListViewOnScroll() {
        if (listView!=null) {
            listView.setOnScrollListener(new OnScrollListener() {
                //正在移動
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    Log.i(CustomSwipeRefreshLayout, ""+listView.getLastVisiblePosition());
                    if (canLoadMore()) {
                        loadData();
                    }else {
                        Log.i(CustomSwipeRefreshLayout, "不可以加載新數據");    
                    }
                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    // TODO Auto-generated method stub

                }
            });
        }
    }
    /**
     * 三個條件可以加載數據
     * 1、滑動距離合適的時候
     * 2、最後一個條目
     * 3、沒有正在加載數據
     * @return
     */
    protected boolean canLoadMore() {
        boolean condition1=false;
        if (!isLoading){
            condition1=true;
        }
        boolean condition2=false;
        if (listView.getLastVisiblePosition()==(listView.getCount()-1)) {
            condition2=true;
        }
        boolean condition3=false;
        if ((startY-endY)>touchInstance) {
            condition3=true;    
        }
        Log.i(CustomSwipeRefreshLayout, "是否正在加載"+condition1+"是否是最後一個並且已經顯示出來"+condition2+"觸摸距離是否合適"+condition3);        
        return condition1&&condition2&&condition3;
    }
    /**
     * 接口回調實現自定義加載數據
     */
    protected void loadData() {

        if (onLoadListener!=null) {
            if (loadLayout!=null) {
                addLoadLayout();//添加footerView
            }
            onLoadListener.onLoad();
        }

    }
    private void addLoadLayout() {
        listView.addFooterView(loadLayout);
        if ( listView.getAdapter() instanceof BaseAdapter) {
            BaseAdapter adapter=(BaseAdapter) listView.getAdapter() ;
            listView.setAdapter(adapter);
            Log.i(CustomSwipeRefreshLayout, "是baseAdapter");
        }else{
            Log.i(CustomSwipeRefreshLayout, "不是baseAdapter");
        }
    }
    private void removeLoadLayout() {
        listView.removeFooterView(loadLayout);
    }

    public void setOnload(boolean isLoad){
        isLoading=isLoad;
        if (!isLoad) {
            removeLoadLayout();
        }
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //按下時
        if (ev.getAction()==MotionEvent.ACTION_DOWN) {
            startY=ev.getY();
        }
        //離開時
        else if(ev.getAction()==MotionEvent.ACTION_UP){
            endY=ev.getY();
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 初始化底部加載視圖
     */
    private void initLoadLayout() {
        //佈局,由於父控件是ListView,所以 LayoutParams 是AbsListView的LayoutParams
        AbsListView.LayoutParams listLayoutParams =new AbsListView.LayoutParams(listView.getLayoutParams()); 
        listLayoutParams.width=LayoutParams.MATCH_PARENT;
        listLayoutParams.height=100;
        loadLayout=new LinearLayout(context);
        loadLayout.setOrientation(LinearLayout.HORIZONTAL);
        loadLayout.setLayoutParams(listLayoutParams);
        loadLayout.setGravity(Gravity.CENTER_HORIZONTAL);
        //dialog
        android.view.ViewGroup.LayoutParams layoutParams =new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT);
        ProgressBar progressBar=new ProgressBar(context,null,android.R.attr.progressBarStyleInverse);
        progressBar.setLayoutParams(layoutParams);
        //textview
        android.view.ViewGroup.LayoutParams layoutParams2 =new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT);
        TextView textView=new TextView(context);
        textView.setText("正在加載.....");
        textView.setTextSize(15);
        textView.setLayoutParams(layoutParams2);
        textView.setGravity(Gravity.CENTER_VERTICAL);
        //設置子控件
        loadLayout.addView(progressBar);
        loadLayout.addView(textView);
    }
     interface OnLoadListener{
        public void onLoad();
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章