1. 程式人生 > >Android下拉重新整理元件—SwipeRefreshLayout

Android下拉重新整理元件—SwipeRefreshLayout

介紹

這裡寫圖片描述
一個功能強大的自定義下拉重新整理元件。
SwipeRefreshLayout也是一種下拉重新整理控制元件,不同的它的重新整理狀態效果和傳統的PuulToRefresh完全不一樣。
SwipeRefreshLayout 是谷歌公司推出的用於下拉重新整理的控制元件,SwipeRefreshLayout已經被放到了sdk中,在Version 19.1之後SwipeRefreshLayout 被放到support v4中。

方法API

setOnRefreshListener(OnRefreshListener):新增下拉重新整理監聽器
setRefreshing(boolean):顯示或者隱藏重新整理進度條
isRefreshing():檢查是否處於重新整理狀態
setColorSchemeResources():設定進度條的顏色主題,最多設定四種,以前的setColorScheme()方法已經棄用了。

簡單使用

1.SwipeRefreshLayout本身自帶下拉重新整理的效果,那麼我們可以選擇在RecyclerView佈局外部巢狀一層SwipeRefreshLayout佈局即可,具體佈局檔案如下:

<?xmlversionxmlversion="1.0" encoding="utf-8"?>  
<LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"android:layout_width="match_parent"
android:layout_height="match_parent"> <includelayoutincludelayout="@layout/common_top_bar_layout"/> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/demo_swiperefreshlayout" android:layout_width="fill_parent" android:layout_height="fill_parent"
android:scrollbars="vertical" > <android.support.v7.widget.RecyclerView android:id="@+id/demo_recycler" android:layout_width="fill_parent" android:layout_height="fill_parent" ></android.support.v7.widget.RecyclerView> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

2.接著在Activity中獲取SwipeRefreshLayout控制元件並且設定OnRefreshListener監聽器,同時實現裡邊的onRefresh()方法,在該方法中進行網路請求最新資料,然後重新整理RecyclerView列表同時設定SwipeRefreshLayout的進度Bar的隱藏或者顯示效果。具體程式碼如下:

demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {  
            @Override  
            public void onRefresh() {  
                Log.d("zttjiangqq","invoke onRefresh...");  
                new Handler().postDelayed(newRunnable() {  
                    @Override  
                    public void run() {  
                        List<String> newDatas = new ArrayList<String>();  
                        for (int i = 0; i <5; i++) {  
                            int index = i + 1;  
                           newDatas.add("new item" + index);  
                        }  
                       adapter.addItem(newDatas);  
                       demo_swiperefreshlayout.setRefreshing(false);  
                       Toast.makeText(RecyclerRefreshActivity.this, "更新了五條資料...", Toast.LENGTH_SHORT).show();  
                    }  
                }, 5000);  
            }  
        });  

SwipeRefreshLayout裡面需要注意的Api:

       1setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener):設定手勢滑動監聽器。

         2setProgressBackgroundColor(int colorRes):設定進度圈的背景色。

         3setColorSchemeResources(int… colorResIds):設定進度動畫的顏色。

         4setRefreshing(Boolean refreshing):設定元件的刷洗狀態。

         5setSize(int size):設定進度圈的大小,只有兩個值:DEFAULT、LARGE

案例使用

原理:是一個重新整理佈局,來自相容包v4可以執行在低版本,控制元件如果想要支援下拉重新整理,只要使用當前佈局包裹
setColorSchemeColors:修改旋轉顏色,可以新增多種顏色
setRefreshing: 是否顯示loading狀態
setOnRefreshListener:下拉重新整理監聽

例子:
public class MainActivity extends AppCompatActivity {
    private SwipeRefreshLayout swipeRefreshLayout;
    protected void onCreate(Bundle savedInstanceState) {
                 ·····
   swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh_layout);
        //設定旋轉的顏色效果
        swipeRefreshLayout.setColorSchemeColors(Color.GREEN, Color.YELLOW, Color.RED);
        //設定下拉重新整理的監聽器
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            public void onRefresh() {
                requestData();
            }
        });
    }
    private void requestData() {
        new Handler().postDelayed(new Runnable() {//延時載入
       public void run() {
                String json = "{name:小米}";
                TextView text = (TextView) findViewById(R.id.text);
                text.setText(json);
                swipeRefreshLayout.setRefreshing(false);//關閉顯示
            }
        }, 2000);
    }

載入更多——上滑載入資料
思路:1.滾動到底部 getItemCount()-2 ==bottomPosition
2.不處理滾動中
OnScrollListener監聽滾動載入的監聽器
int dy 滾動距離,向上滾動為正
layoutManager.findLastVisibleItemPosition獲取處於底部資料的下標
dy>0與isLoading 都是用來控制元件靈敏度

public class HomeFragment extends Fragment {
        private SwipeRefreshLayout swipeRefreshLayout;
        private RecyclerView recyclerView;
        private AppAdapter adapter;
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
                //有一個顯示網路狀態
                //StateLayout stateLayout  = new StateLayout(container.getContext());
            swipeRefreshLayout = new SwipeRefreshLayout(container.getContext());
            //設定顏色
            swipeRefreshLayout.setColorSchemeColors(Color.GREEN);
            //設定下拉監聽
            swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    HttpConnect.get(ApiUrls.HOME+"?index=0", callback);
                }
            });
            recyclerView = new RecyclerView(container.getContext());
            //設定排列規則
            recyclerView.setLayoutManager(new LinearLayoutManager(container.getContext()));
            //預先設定了Noemal檢視,但是normal檢視沒有內容    stateLayout.addNormalView(recyclerView);
            swipeRefreshLayout.addView(recyclerView);
            //傳送請求給伺服器
            HttpConnect.get(ApiUrls.HOME+"?index=0",callback);
            return swipeRefreshLayout;
        }
        DefaultCallBack callback = new DefaultCallBack() {
            @Override
            public void onStart(int what) {
                swipeRefreshLayout.setRefreshing(true);
            }
            @Override
            public void onFinish(int what) {
                swipeRefreshLayout.setRefreshing(false);
            }
            //拿到資料
            protected void createView(String json) {
                HomeWebInfo info = new Gson().fromJson(json, HomeWebInfo.class);
                //建立控制元件,設定介面卡
                adapter=new AppAdapter(info.list);
                recyclerView.setAdapter(adapter);
                //上滑載入資料
                addLoadMoreList();
            }
        };
        private void addLoadMoreList(){
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
            private boolean isLoading = false;
           //int dy 上下滑動的距離 +數代表往上滑動
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                //判斷 是否是排列布局
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                //判斷是否是線性排列,並且是否滑動距離
                if(layoutManager instanceof  LinearLayoutManager && dy>0 ){
                    //當前是列表
                    int total = adapter.getItemCount();
                    int lastPosition = total-2;
                    //獲取rv 的bottom條目
                    int currLastPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                    if(currLastPosition==lastPosition&&!isLoading){      }
                    isLoading=true;
                    Toast.makeText(MyApp.getContext(),"載入更多...",Toast.LENGTH_SHORT).show();
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MyApp.getContext(), "載入成功", Toast.LENGTH_SHORT).show();
                            isLoading=false;
                        }
                    },5000);
                }
            }
        });
      }
  }

開源框架RecyclerRefreshLayout

這裡寫圖片描述
原始碼點選檢視
RecyclerRefreshLayout 基於 {@link android.support.v4.widget.SwipeRefreshLayout} RecyclerRefreshLayout 可以被用於使用者通過一個垂直滑動的手勢重新整理當前 view 內容的場景. 例項化這個檢視的操作中總應該新增一個OnRefreshListener監聽重新整理動作完成後通知。 RecyclerRefreshLayout 每次手勢操作完成後都會通知監聽器(OnRefreshListener), 監聽器負責決定何時來發起一個重新整理操作。 如果監聽器決定不觸發重新整理操作則需要呼叫 setRefreshing(false) 取消重新整理的任何可視指示. 如果使用者想要強制執行一次重新整理動畫, 則需要呼叫 setRefreshing(true). 如果想要禁用重新整理動畫,則可以呼叫 setEnabled(false)。

注意: RecyclerRefreshLayout 支援所有的View: ListView, GridView, ScrollView, FrameLayout, 甚至一個 TextView

在你的build.gradle檔案裡新增下面依賴:

  dependencies {
        compile 'com.dinuscxj:recyclerrefreshlayout:2.0.3'
    }

在xml中配置

<?xml version="1.0" encoding="utf-8"?>
<com.dinuscxj.refresh.RecyclerRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/refresh_layout"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 <android.support.v7.widget.RecyclerView
     android:id="@+id/recycler_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent" />
</app.dinus.com.refresh.RecyclerRefreshLayout>

配置下面的屬性(* 必選)

設定監聽器監聽手勢操作是否觸發重新整理操作

RecyclerRefreshLayout.setOnRefreshListener(OnRefreshListener);

通知重新整理狀態已經改變. 當重新整理由滑動手勢觸發不要呼叫它。

RecyclerRefreshLayout.setRefreshing(boolean);

配置下面的屬性(可選)

設定將重新整理動畫從手勢釋放的位置移動到重新整理位置的動畫所需要的Interpolator

RecyclerRefreshLayout.setAnimateToRefreshInterpolator(Interpolator);

設定將重新整理動畫從手勢釋放的位置(或重新整理位置)移動到起始位置的動畫所需要的Interpolator

RecyclerRefreshLayout.setAnimateToStartInterpolator(Interpolator);

設定將重新整理動畫從手勢釋放的位置移動到重新整理位置的動畫所需要的時間

RecyclerRefreshLayout.setAnimateToRefreshDuration(int);

設定將重新整理動畫從手勢釋放的位置(或重新整理位置)移動到起始位置的動畫所需要的時間

RecyclerRefreshLayout.setAnimateToStartDuration(int);

設定RefreshView相對父元件的初始位置

RecyclerRefreshLayoutsetRefreshInitialOffset(float)

設定觸發重新整理需要滑動的最小距離

RecyclerRefreshLayout.setRefreshTargetOffset(float)

設定RefreshView的樣式

RecyclerRefreshLayout.setRefreshStyle(@NonNull RefreshStyle) 

自定義
為 RecyclerRefreshLayout 自定義重新整理動畫 (需要實現介面 IRefreshStatus)

public interface IRefreshStatus {
/**
* 當手勢操作完成且重新整理動畫滑動到起始位置
*/
void reset();
/**
* 正在重新整理
*/
void refreshing();
/**
* 重新整理動畫被下拉到重新整理位置
*/
void pullToRefresh();
/**
* 重新整理動畫被釋放到重新整理位置
*/
void releaseToRefresh();
/**
* @param pullDistance 重新整理動畫被拖動的距離
* @param pullProgress 重新整理動畫被拖動的進度,當大於觸發重新整理的距離會大於1.0f
*                     pullProgress = pullDistance / refreshTargetOffset
*/
void pullProgress(float pullDistance, float pullProgress);
}

自定義網路載入圖片控制元件—StateLayout:

需求:需要一個狀態控制元件,1:自定義控制元件 繼承ViewGroup/View 2:底層都使用Paint Canvas或者ImageView TextView

//步驟一 。完成建構函式
public class StateLayout extends FrameLayout {
    private View mLoading = null;
    private View mError = null;
    private View mEmpty = null;
    private View mNormal = null;

    public StateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //步驟二 將佈局打氣進來並且作為一個元素添加當前的容器
        View view = View.inflate(context, R.layout.state_layout, null);
        mLoading = view.findViewById(R.id.loading);
        mError = view.findViewById(R.id.error);
        mEmpty = view.findViewById(R.id.nodata);
//        mNormal = view.findViewById(R.id.normal);
        this.addView(view);
    }

    public StateLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public StateLayout(Context context) {
        this(context, null, 0);
    }

    //三。重置讓所有的控制元件變成gone
    private void reset() {
        mLoading.setVisibility(View.GONE);
        mError.setVisibility(View.GONE);
        mEmpty.setVisibility(View.GONE);
        if (mNormal != null) {
            mNormal.setVisibility(View.GONE);
        }

    }

    //顯示載入中
    public void showLoading() {
        reset();
        mLoading.setVisibility(View.VISIBLE);
    }

    //顯示網路出錯
    public void showError() {
        reset();
        mError.setVisibility(View.VISIBLE);
    }

    //顯示空
    public void showEmpty() {
        reset();
        mEmpty.setVisibility(View.VISIBLE);
    }

    public void addNormalView(Object normalViewLayout) {
        if (normalViewLayout instanceof Integer) {
            mNormal = LayoutInflater.from(getContext()).inflate((Integer) normalViewLayout, this, false);
        } else {
            mNormal = (View) normalViewLayout;
        }
        this.addView(mNormal);//只有新增到當前佈局的元素才可以顯示
    }

    //顯示正常
    public void showNormal() {
        reset();
        mNormal.setVisibility(View.VISIBLE);
    }

}