編輯:關於android開發
注:本文demo已經提交github,地址完整代碼如下,demo工程已經上傳至GitHub,
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
感謝大家支持!
在Android開發中,我們經常會用到列表下拉刷新和上拉加載的功能。
Google在support.v4包中提供了一個組件可以用來進行下來刷新,這個組件是SwipeRefreshLayout。
下面我們來看一下這個組件的使用:
在布局文件中加上xml代碼
在MainActivity.java代碼中添加代碼:
/**
* 添加變量
* /
private SwipeRefreshLayout swipeRefreshLayout;
private ListView listView;
private ArrayList list = new ArrayList<>();
private ArrayAdapter adapter;
在Activity的onCreate方法中初始化實例
/**
* 實例化變量,此處省略initList()方法
* 此方法內容為添加了N個字符串"item"
*/
initList();
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
listView = (ListView) findViewById(R.id.list_view);
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
接下來給SwipeRefreshLayout添加OnRefreshListener監聽
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Toast.makeText(MainActivity.this, "is refreshing!", Toast.LENGTH_SHORT).show();
}
});
運行效果如圖所示
但是在日常開發中,我們不僅需要下拉刷新的功能,還應該有上拉繼續加載的功能,但是Google並沒有給我們提供上拉加載的方法,那怎麼辦呢?我們可以自定義一個View,自己添加上拉加載方法。
我們模仿Swiperefreshlayout的使用下拉刷新的方法,去設計上拉加載的方法。添加了上拉加載的代碼形式大約為
view.setOnLoadListener(new OnLoadListener() {
@Override
public void onLoad() {
//在此執行上拉加載的邏輯
view.setloading(false); //邏輯執行完畢後停止上拉加載
}
});
所以,我們在重寫下拉刷新組件前先寫一個接口OnLoadListener
public interface OnLoadListener {
public void onLoad(); //需要用戶重寫的方法
}
我們寫一個UpLoadSwipeRefreshLayout類,繼承SwipeRefershLayout類。我們應該在什麼時候開始上拉加載呢?當然是在子ListView滑動到最底部的時候,所以,我們可以開始進行上拉加載的時候應該滿足一下條件:
① 判斷listview已經滑動到了最底部
② 判斷手指是在向上滑動
③ 當前上拉加載不正在進行
編寫類
代碼如下:
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout {
private ListView mListView; //子view
private boolean isCanLoading = false; //是否正可以加載,默認不可用
private OnLoadListener mOnLoadListener;
private View mFooterView; //上拉加載的視圖
private int firstY;
private int lastY;
private int mTouchSlop; //最短的滑動距離
private int footerResource; //加載視圖的資源ID
public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //獲取系統默認的最短滑動距離
}
}
因為ListView是此組件的子view,那麼我們應該在開始的時候去檢測,子view是否是listview。
重寫onLayout()方法,獲取子類
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mListView == null) {
getListView(); //獲取子view
}
}
構造一個getListView方法
private void getListView() {
int childs = getChildCount();
if (childs > 0) {
View childView = getChildAt(0); //獲取第一個子view
if (childView instanceof ListView) {
/**
* 如果子view的類型是ListView
*/
mListView = (ListView) childView;
//給子listview設置滑動監聽
mListView.setOnScrollListener(this);
}
}
}
重寫事件攔截事件,防止滑動事件發生沖突
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
/**
* 獲得觸摸事件剛剛開始的時候,手指觸摸的坐標
*/
firstY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
/**
* 獲得觸摸事件結束的時候,手指離開時候的坐標
*/
lastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (isCanLoad()) {
loadData();
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
為了獲取子listview的滑動情況,使用我們應該讓組件實現OnScrollListener接口。
即
class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
/**
* 重寫滑動時間
* /
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isCanLoad()) {
/**
* 滑動過程中如果可以加載數據
* 就執行加載數據的函數
*/
loadData();
}
}
}
編寫isCanLoad()方法,這裡就用到了上方我們需要滿足的三個條件
private boolean isCanLoad() {
return isBottom() && isPushUp() && !isCanLoading;
}
依次編寫2個方法
/**
* 判斷listview是否到了底部
*/
private boolean isBottom() {
if (mListView != null && mListView.getAdapter() != null) {
/**
* 當前可加的最後一項就是listview的末尾項
* 說明滑動到了最底部
*/
return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
}
return false;
}
/**
* 判斷是否正在上拉
*/
private boolean isPushUp() {
/**
* 往上滑動的距離大於系統默認的滑動距離
*/
return firstY - lastY >= mTouchSlop;
}
這時候我們還需要編寫loadData()方法
/**
* 加載數據
*/
private void loadData() {
if (mOnLoadListener != null) {
setLoading(true);
/**
* 處理加載的邏輯
* 具體邏輯在回調中完成
*/
mOnLoadListener.onLoad();
}
}
編寫設置可加載的方法setLoading()
public void setLoading(boolean isCanLoad) {
this.isCanLoading = isCanLoad;
if (isCanLoad == true) {
/**
* 加載,添加底部的正在加載視圖
*/
mListView.addFooterView(mFooterView);
} else {
/**
* 不在加載中,將底部的加載中視圖移除
*/
mListView.removeFooterView(mFooterView);
firstY = 0;
lastY = 0;
}
}
對了,還沒添加設置上拉加載監聽的方法!
編寫setOnUpLoadListener()方法
public void setOnLoadListener(OnLoadListener onLoadListener) {
this.mOnLoadListener = onLoadListener;
}
這個類我對外提供了一個變量,代表listview的底視圖的資源id
可以通過setFooterResource方法進行添加並實例化此底部view
編寫此方法
public void setFooterResource(int footerResource) {
this.footerResource = footerResource;
/**
* 實例化布局view
*/
this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false);
}
這時候這個類我們就編寫好了,完整的代碼我會在博客的結尾放出。
我們先來看看這段代碼的使用
將剛才的布局文件修改成如下:
添加尾視圖的資源id文件,layout_up_load_view.xml
修改MainActivity.java的代碼
swipeRefreshLayout.setFooterResource(R.layout.layout_up_load_view); //添加正在加載中的視圖
swipeRefreshLayout.setOnLoadListener(new OnLoadListener() {
@Override
public void onLoad() {
//上拉加載的操作
}
});
運行效果如圖:
完整代碼如下,demo工程已經上傳至GitHub
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
package com.example.chenglei.myapplication;
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by chenglei on 2016/4/20.
* 重寫下拉刷新組件,添加上拉加載的功能
*/
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {
private ListView mListView; //子view
private boolean isCanLoading = false; //是否正可以加載,默認不可用
private OnLoadListener mOnLoadListener;
private View mFooterView; //上拉加載的視圖
private int firstY;
private int lastY;
private int mTouchSlop; //最短的滑動距離
private int footerResource;
public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //獲取系統默認的最短滑動距離
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mListView == null) {
getListView();
}
}
/**
* 獲取子view
*/
private void getListView() {
int childs = getChildCount();
if (childs > 0) {
View childView = getChildAt(0); //獲取第一個子view
if (childView instanceof ListView) {
/**
* 如果子view的類型是ListView
*/
mListView = (ListView) childView;
//給子listview設置滑動監聽
mListView.setOnScrollListener(this);
}
}
}
/**
* 觸摸事件攔截
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
/**
* 獲得觸摸事件剛剛開始的時候,手指觸摸的坐標
*/
firstY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
/**
* 獲得觸摸事件結束的時候,手指離開時候的坐標
*/
lastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (isCanLoad()) {
loadData();
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 判斷是否可以進行加載
*/
private boolean isCanLoad() {
return isBottom() && isPushUp() && !isCanLoading;
}
/**
* 判斷listview是否到了底部
*/
private boolean isBottom() {
if (mListView != null && mListView.getAdapter() != null) {
/**
* 當前可加的最後一項就是listview的末尾項
* 說明滑動到了最底部
*/
return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
}
return false;
}
/**
* 判斷是否正在上拉
*/
private boolean isPushUp() {
/**
* 往上滑動的距離大於系統默認的滑動距離
*/
return firstY - lastY >= mTouchSlop;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isCanLoad()) {
/**
* 滑動過程中如果可以加載數據
* 就執行加載數據的函數
*/
loadData();
}
}
/**
* 加載數據
*/
private void loadData() {
if (mOnLoadListener != null) {
setLoading(true);
/**
* 處理加載的邏輯
* 具體邏輯在回調中完成
*/
mOnLoadListener.onLoad();
}
}
/**
* 設置可加載
*/
public void setLoading(boolean isCanLoad) {
this.isCanLoading = isCanLoad;
if (isCanLoad == true) {
/**
* 加載,添加底部的正在加載視圖
*/
mListView.addFooterView(mFooterView);
} else {
/**
* 不在加載中,將底部的加載中視圖移除
*/
mListView.removeFooterView(mFooterView);
firstY = 0;
lastY = 0;
}
}
/**
* 設置上拉加載監聽
*/
public void setOnLoadListener(OnLoadListener onLoadListener) {
this.mOnLoadListener = onLoadListener;
}
/**
* 設置上拉加載的布局
*/
public void setFooterResource(int footerResource) {
this.footerResource = footerResource;
/**
* 實例化布局view
*/
this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false);
}
}
【React Native開發】React Native控件之ProgressBarAndroid進度條講解(12) (一)前言 今天我們一起來看一下進度加載條Pro
前面兩節講了常用Layout之FrameLayout、LinearLayout
《Android Studio實用指南》8.20 提煉接口 提煉接口重構是從一個已存在的類中提煉接口,它可以從某個類中選擇方法,把選中的方法提取到一個單獨的接口中. 操
【React Native開發】React Native控件之ViewPagerAndroid講解以及美團首頁頂部效果實例(17) (一)前言 今天我們一起來看一下V