Android刷新加載框架詳解
本文實(shí)例為大家分享了Android刷新加載框架的具體代碼,供大家參考,具體內(nèi)容如下
1.定義一個(gè)接口控制下拉和上拉
public interface Pullable { /** * 是否可下拉 */ boolean canPullDown(); /** * 是否可上拉 */ boolean canPullUp(); }
2.定義一個(gè)刷新加載布局
public class PullToRefreshLayout extends RelativeLayout { /** * 頭 */ private View headView;//頭視圖 private ImageView headIv;//頭圖標(biāo) private TextView headTv;//頭文字 private int headHeight;//頭高度 private float headBorder;//頭臨界 /** * 拉 */ private View pullView;//拉視圖 private int pullHeight;//拉高度 private int pullWidth;//拉寬度 /** * 尾 */ private View footView;//尾視圖 private ImageView footIv;//尾圖標(biāo) private TextView footTv;//尾文字 private int footHeight;//尾高度 private float footBorder;//尾臨界 /** * 狀態(tài) */ public static final int INIT = 0;//初始 public static final int RELEASE_TO_REFRESH = 1;//釋放刷新 public static final int REFRESHING = 2;//正在刷新 public static final int RELEASE_TO_LOAD = 3;//釋放加載 public static final int LOADING = 4;//正在加載 public static final int DONE = 5;//完成 private int state = INIT; /** * 接口 */ private OnRefreshListener mListener; private float downY;//按下時(shí)Y坐標(biāo) private float lastY;//上一個(gè)Y坐標(biāo) private float pullDownY = 0;//下拉偏移量 private float pullUpY = 0;//上拉偏移量 private int offset;//偏移量 /** * 動(dòng)畫 */ private RotateAnimation rotateAnimation; /** * 線程 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg != null) { switch (msg.what) { case 1: headIv.clearAnimation(); break; case 2: footIv.clearAnimation(); break; default: break; } pullDownY = 0; pullUpY = 0; requestLayout(); state = INIT; refreshViewByState(); isTouch = true; } } }; /** * 第一次執(zhí)行布局 */ private boolean isLayout = false; /** * 在刷新過程中滑動(dòng)操作 */ private boolean isTouch = false; /** * 手指滑動(dòng)距離與下拉頭的滑動(dòng)距離比,中間會(huì)隨正切函數(shù)變化 */ private float radio = 2; /** * 過濾多點(diǎn)觸碰 */ private int mEvents; /** * 這兩個(gè)變量用來控制pull的方向,如果不加控制,當(dāng)情況滿足可上拉又可下拉時(shí)沒法下拉 */ private boolean canPullDown = true; private boolean canPullUp = true; public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; } public PullToRefreshLayout(Context context) { super(context); initView(context); } public PullToRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } private void initView(Context context) { rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.rotating); } private void refreshViewByState() { switch (state) { case INIT: // 下拉布局初始狀態(tài) headIv.setImageResource("下拉刷新顯示的圖片"); headTv.setText("下拉刷新"); // 上拉布局初始狀態(tài) footIv.setImageResource("上拉加載顯示的圖片"); footTv.setText("上拉加載"); break; case RELEASE_TO_REFRESH: // 釋放刷新狀態(tài) headIv.setImageResource("釋放刷新顯示的圖片"); headTv.setText("釋放刷新"); break; case REFRESHING: // 正在刷新狀態(tài) headIv.setImageResource("正在刷新顯示的圖片"); headTv.setText("正在刷新"); break; case RELEASE_TO_LOAD: // 釋放加載狀態(tài) footIv.setImageResource("釋放加載顯示的圖片"); footTv.setText("釋放加載"); break; case LOADING: // 正在加載狀態(tài) footIv.setImageResource("正在加載顯示的圖片"); footTv.setText("正在加載"); break; case DONE: // 刷新或加載完畢,啥都不做 break; } } /** * 不限制上拉或下拉 */ private void releasePull() { canPullDown = true; canPullUp = true; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); lastY = downY; mEvents = 0; releasePull(); if (state != REFRESHING && state != LOADING) { isTouch = true; } break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: // 過濾多點(diǎn)觸碰 mEvents = -1; break; case MotionEvent.ACTION_MOVE: if (mEvents == 0) { if (pullDownY > 0 || (((Pullable) pullView).canPullDown() && canPullDown && state != LOADING && state != REFRESHING)) { // 可以下拉,正在加載時(shí)不能下拉 // 對(duì)實(shí)際滑動(dòng)距離做縮小,造成用力拉的感覺 pullDownY = pullDownY + (ev.getY() - lastY) / radio; if (pullDownY < 0) { pullDownY = 0; canPullDown = false; canPullUp = true; } if (pullDownY > getMeasuredHeight()) { pullDownY = getMeasuredHeight(); } if (state == REFRESHING) { // 正在刷新的時(shí)候觸摸移動(dòng) isTouch = false; } } else if (pullUpY < 0 || (((Pullable) pullView).canPullUp() && canPullUp && state != REFRESHING && state != LOADING)) { // 可以上拉,正在刷新時(shí)不能上拉 pullUpY = pullUpY + (ev.getY() - lastY) / radio; if (pullUpY > 0) { pullUpY = 0; canPullDown = true; canPullUp = false; } if (pullUpY < -getMeasuredHeight()) { pullUpY = -getMeasuredHeight(); } if (state == LOADING) { // 正在加載的時(shí)候觸摸移動(dòng) isTouch = false; } } } if (isTouch) { lastY = ev.getY(); if (pullDownY > 0 || pullUpY < 0) { requestLayout(); } if (pullDownY > 0) { if (pullDownY <= headBorder && (state == RELEASE_TO_REFRESH || state == DONE)) { // 如果下拉距離沒達(dá)到刷新的距離且當(dāng)前狀態(tài)是釋放刷新,改變狀態(tài)為下拉刷新 state = INIT; refreshViewByState(); } if (pullDownY >= headBorder && state == INIT) { // 如果下拉距離達(dá)到刷新的距離且當(dāng)前狀態(tài)是初始狀態(tài)刷新,改變狀態(tài)為釋放刷新 state = RELEASE_TO_REFRESH; refreshViewByState(); } } else if (pullUpY < 0) { // 下面是判斷上拉加載的,同上,注意pullUpY是負(fù)值 if (-pullUpY <= footBorder && (state == RELEASE_TO_LOAD || state == DONE)) { state = INIT; refreshViewByState(); } // 上拉操作 if (-pullUpY >= footBorder && state == INIT) { state = RELEASE_TO_LOAD; refreshViewByState(); } } // 因?yàn)樗⑿潞图虞d操作不能同時(shí)進(jìn)行,所以pullDownY和pullUpY不會(huì)同時(shí)不為0,因此這里用(pullDownY + // Math.abs(pullUpY))就可以不對(duì)當(dāng)前狀態(tài)作區(qū)分了 if ((pullDownY + Math.abs(pullUpY)) > 8) { // 防止下拉過程中誤觸發(fā)長(zhǎng)按事件和點(diǎn)擊事件 ev.setAction(MotionEvent.ACTION_CANCEL); } } break; case MotionEvent.ACTION_UP: if (pullDownY > headBorder || -pullUpY > footBorder) { // 正在刷新時(shí)往下拉(正在加載時(shí)往上拉),釋放后下拉頭(上拉頭)不隱藏 isTouch = false; } if (state == RELEASE_TO_REFRESH) { state = REFRESHING; refreshViewByState(); // 刷新操作 if (mListener != null) { canPullDown = false; pullDownY = headBorder; pullUpY = 0; requestLayout(); headIv.startAnimation(rotateAnimation); mListener.onRefresh(this); } } else if (state == RELEASE_TO_LOAD) { state = LOADING; refreshViewByState(); // 加載操作 if (mListener != null) { canPullUp = false; pullDownY = 0; pullUpY = -footBorder; requestLayout(); footIv.startAnimation(rotateAnimation); mListener.onLoadMore(this); } } else { pullDownY = 0; pullUpY = 0; requestLayout(); } default: break; } // 事件分發(fā)交給父類 super.dispatchTouchEvent(ev); return true; } public void hideHeadView() { handler.sendEmptyMessage(1); } public void hideFootView() { handler.sendEmptyMessage(2); } private void initView() { // 初始化下拉布局 headIv = (ImageView) headView.findViewById(R.id.iv_head); headTv = (TextView) headView.findViewById(R.id.tv_head); //初始化上拉布局 footIv = (ImageView) footView.findViewById(R.id.iv_foot); footTv = (TextView) footView.findViewById(R.id.tv_foot); refreshViewByState(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (!isLayout) { // 這里是第一次進(jìn)來的時(shí)候做一些初始化 headView = getChildAt(0); pullView = getChildAt(1); footView = getChildAt(2); headBorder = ((ViewGroup) headView).getChildAt(0).getMeasuredHeight(); footBorder = ((ViewGroup) footView).getChildAt(0).getMeasuredHeight(); headHeight = headView.getMeasuredHeight(); pullHeight = pullView.getMeasuredHeight(); footHeight = footView.getMeasuredHeight(); pullWidth = pullView.getMeasuredWidth(); initView(); isLayout = true; } // 改變子控件的布局,這里直接用(pullDownY + pullUpY)作為偏移量,這樣就可以不對(duì)當(dāng)前狀態(tài)作區(qū)分 offset = (int) (pullDownY + pullUpY); headView.layout(0, -headHeight + offset, pullWidth, offset); pullView.layout(0, offset, pullWidth, pullHeight + offset); footView.layout(0, pullHeight + offset, pullWidth, pullHeight + footHeight + offset); } public interface OnRefreshListener { void onRefresh(PullToRefreshLayout pullToRefreshLayout); void onLoadMore(PullToRefreshLayout pullToRefreshLayout); } }
3.自定義View
ListView
public class PullableListView extends ListView implements Pullable { public PullableListView(Context context) { super(context); } public PullableListView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { if (getCount() == 0) { // 沒有item的時(shí)候也可以下拉刷新 return false; } else if (getFirstVisiblePosition() == 0 && getChildAt(0).getTop() >= 0) { // 滑到ListView的頂部了 return true; } else return false; } @Override public boolean canPullUp() { if (getCount() == 0) { // 沒有item的時(shí)候也可以上拉加載 return false; } else if (getLastVisiblePosition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null && getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) .getBottom() <= getMeasuredHeight()) return true; } return false; } }
GridView
public class PullableGridView extends GridView implements Pullable { public PullableGridView(Context context) { super(context); } public PullableGridView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableGridView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { if (getCount() == 0) { // 沒有item的時(shí)候也可以下拉刷新 return false; } else if (getFirstVisiblePosition() == 0 && getChildAt(0).getTop() >= 0) { // 滑到頂部了 return true; } else return false; } @Override public boolean canPullUp() { if (getCount() == 0) { // 沒有item的時(shí)候也可以上拉加載 return false; } else if (getLastVisiblePosition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null && getChildAt( getLastVisiblePosition() - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight()) return true; } return false; } }
RecyclerView
public class PullableRecyclerView extends RecyclerView implements Pullable { public PullableRecyclerView(Context context) { super(context); } public PullableRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public PullableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { RecyclerView.LayoutManager layoutManager = getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; if (linearLayoutManager.getItemCount() == 0) { return false; } else if (linearLayoutManager.findFirstVisibleItemPosition() == 0 && linearLayoutManager.getChildAt(0).getTop() >= 0) { return true; } else { return false; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (staggeredGridLayoutManager.getItemCount() == 0) { return false; } else { int[] firstVisibleItems = null; firstVisibleItems = staggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems); if (firstVisibleItems != null && firstVisibleItems.length > 0) { if (staggeredGridLayoutManager.getChildCount() + firstVisibleItems[0] == staggeredGridLayoutManager.getItemCount()) { return true; } } } } return false; } @Override public boolean canPullUp() { RecyclerView.LayoutManager layoutManager = getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; if (linearLayoutManager.getItemCount() == 0) { return false; } else if (linearLayoutManager.findLastVisibleItemPosition() == (linearLayoutManager.getItemCount() - 1)) { if (linearLayoutManager.getChildAt(linearLayoutManager.findLastVisibleItemPosition() - linearLayoutManager.findFirstVisibleItemPosition()) != null && linearLayoutManager.getChildAt(linearLayoutManager.findLastVisibleItemPosition() - linearLayoutManager.findFirstVisibleItemPosition()).getBottom() <= getMeasuredHeight()) { return true; } } } else if (layoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (staggeredGridLayoutManager.getItemCount() == 0) { return false; } else { int[] lastPositions = new int[staggeredGridLayoutManager.getSpanCount()]; lastPositions = staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions); if (findMax(lastPositions) >= staggeredGridLayoutManager.getItemCount() - 1) { return true; } } } return false; } private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } }
ExpandableListView
public class PullableExpandableListView extends ExpandableListView implements Pullable { public PullableExpandableListView(Context context) { super(context); } public PullableExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableExpandableListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { if (getCount() == 0) { // 沒有item的時(shí)候也可以下拉刷新 return false; } else if (getFirstVisiblePosition() == 0 && getChildAt(0).getTop() >= 0) { // 滑到頂部了 return true; } else return false; } @Override public boolean canPullUp() { if (getCount() == 0) { // 沒有item的時(shí)候也可以上拉加載 return false; } else if (getLastVisiblePosition() == (getCount() - 1)) { // 滑到底部了 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null && getChildAt( getLastVisiblePosition() - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight()) return true; } return false; } }
ScrollView
public class PullableScrollView extends ScrollView implements Pullable { public PullableScrollView(Context context) { super(context); } public PullableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { if (getScrollY() == 0) return true; else return false; } @Override public boolean canPullUp() { if (getScrollY() >= (getChildAt(0).getHeight() - getMeasuredHeight())) return true; else return false; } }
WebView
public class PullableWebView extends WebView implements Pullable { public PullableWebView(Context context) { super(context); } public PullableWebView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableWebView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { if (getScrollY() == 0) return true; else return false; } @Override public boolean canPullUp() { if (getScrollY() >= getContentHeight() * getScale() - getMeasuredHeight()) return true; else return false; } }
ImageView
public class PullableImageView extends ImageView implements Pullable { public PullableImageView(Context context) { super(context); } public PullableImageView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { return true; } @Override public boolean canPullUp() { return true; } }
TextView
public class PullableTextView extends TextView implements Pullable { public PullableTextView(Context context) { super(context); } public PullableTextView(Context context, AttributeSet attrs) { super(context, attrs); } public PullableTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean canPullDown() { return true; } @Override public boolean canPullUp() { return true; } }
4.使用示例(以ListView為例)
<view.PullToRefreshLayout android:id="@+id/ptrl" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/head" /> <view.PullableListView android:id="@+id/plv" android:layout_width="match_parent" android:layout_height="match_parent" /> <include layout="@layout/foot" /> </view.PullToRefreshLayout>
head
<?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/head_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/darker_gray"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:paddingBottom="20dp" android:paddingTop="20dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"> <ImageView android:id="@+id/iv_head" android:layout_width="20dp" android:layout_height="20dp" android:layout_centerVertical="true" android:layout_marginLeft="60dp" android:scaleType="fitXY" /> <TextView android:id="@+id/tv_head" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@android:color/white" android:textSize="16sp" /> </RelativeLayout> </RelativeLayout> </RelativeLayout>
foot
<?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/loadmore_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/darker_gray"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:paddingBottom="20dp" android:paddingTop="20dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"> <ImageView android:id="@+id/iv_foot" android:layout_width="20dp" android:layout_height="20dp" android:layout_centerVertical="true" android:layout_marginLeft="60dp" android:scaleType="fitXY" /> <TextView android:id="@+id/tv_foot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@android:color/white" android:textSize="16sp" /> </RelativeLayout> </RelativeLayout> </RelativeLayout>
4.注意
自定義的View跟正常的View的使用沒有什么差別
如需實(shí)現(xiàn)刷新加載,必須使ptrl.setOnRefreshListener(PullToRefreshLayout.OnRefreshListener onRefreshListener);
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android下拉刷新上拉加載控件(適用于所有View)
- Android實(shí)現(xiàn)上拉加載更多以及下拉刷新功能(ListView)
- Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android RecyclerView 上拉加載更多及下拉刷新功能的實(shí)現(xiàn)方法
- Android ListView實(shí)現(xiàn)上拉加載更多和下拉刷新功能
- Android使用recyclerview打造真正的下拉刷新上拉加載效果
- Android開發(fā)之ListView列表刷新和加載更多實(shí)現(xiàn)方法
- Android ListView下拉刷新上拉自動(dòng)加載更多DEMO示例
- android教你打造獨(dú)一無二的上拉下拉刷新加載框架
相關(guān)文章
Android防止點(diǎn)擊過快造成多次響應(yīng)事件的解決方法
btn點(diǎn)擊用戶可能只點(diǎn)擊了一次但是后臺(tái)響應(yīng)了多次,像一些表單的提交出現(xiàn)這種問題比較棘手,本篇文章主要介紹Android防止點(diǎn)擊過快造成多次響應(yīng)事件的解決方法,有興趣的可以了解一下。2016-12-12Android Studio里如何使用lambda表達(dá)式
這篇文章主要介紹了Android Studio里如何使用lambda表達(dá)式,需要的朋友可以參考下2017-05-05Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案
這篇文章主要為大家詳細(xì)介紹了Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android自定義View實(shí)現(xiàn)柱狀波形圖的繪制
柱狀波形圖是一種常見的圖形。一個(gè)個(gè)柱子按順序排列,構(gòu)成一個(gè)波形圖。本文將利用Android自定義View實(shí)現(xiàn)柱狀波形圖的繪制,需要的可以參考一下2022-08-08Android UI 之實(shí)現(xiàn)多級(jí)樹形列表TreeView示例
這篇文章主要介紹了Android UI 之實(shí)現(xiàn)多級(jí)列表TreeView示例,TreeView就是在Windows中常見的多級(jí)列表樹,有興趣的可以了解一下。2017-03-03Android圖片三級(jí)緩存策略(網(wǎng)絡(luò)、本地、內(nèi)存緩存)
這篇文章主要介紹了Android圖片三級(jí)緩存策略(網(wǎng)絡(luò)、本地、內(nèi)存緩存)的相關(guān)資料,需要的朋友可以參考下2016-04-04