這篇文章主要介紹Android如何實(shí)現(xiàn)RecyclerView下拉刷新效果,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
成都創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括宜章網(wǎng)站建設(shè)、宜章網(wǎng)站制作、宜章網(wǎng)頁(yè)制作以及宜章網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,宜章網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到宜章省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
具體內(nèi)容如下
思路
RealPullRefreshView繼承了一個(gè)LinearLayout
里面放置了一個(gè)刷新頭布局,將其margin_top設(shè)置為負(fù)的刷新頭的高度的
再添加一個(gè)RecyclerView
觸摸事件分發(fā)機(jī)制,當(dāng)在特定條件下讓RealPullRefreshView攔截觸摸事件,否則的話,不攔截,讓RecyclerView自己去處理觸摸事件
在手指下拉時(shí),定義好不同的狀態(tài)STATE,在不同狀態(tài)下,處理不同的顯示,這里講不同狀態(tài)下的刷新頭如何顯示,抽象為一個(gè)接口,用戶可以實(shí)現(xiàn)這個(gè)接口,自定義刷新頭的布局和動(dòng)畫
加載更多的功能是利用RecyclerView的多type布局實(shí)現(xiàn)的
難點(diǎn)在于觸摸事件的攔截,和認(rèn)真處理各種滑動(dòng)的問(wèn)題
使用
xml
<com.example.apple.quickdemo.realview.view.RealPullRefreshView android:id="@+id/real_pull_refresh_view" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:refresh_header_view="@layout/headerview"/>
這是headerview
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ff0" android:orientation="horizontal" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@color/colorAccent" android:visibility="visible" android:id="@+id/tv" android:gravity="center" android:text="下拉刷新" android:textSize="21sp"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" android:id="@+id/iv" /> </LinearLayout>
代碼
mRealPullRefreshView.setLayoutManager(mLayoutManager); mRealPullRefreshView.setAdapter(mMyAdapte); //用戶可以自定義自己的刷新頭布局和動(dòng)畫 //mRealPullRefreshView.setOnPullShowViewListener(new GifOnPullShowViewListerner(mRealPullRefreshView)); mRealPullRefreshView.setOnPullListener(new RealPullRefreshView.OnPullListener() { @Override public void onRefresh() { mHandler.postDelayed(new Runnable() { @Override public void run() { mBodies.add(0, new Body("新數(shù)據(jù)"+i++,100)); mRealPullRefreshView.refreshFinish(); } }, 3000); } @Override public void onLoadMore() { final List<Body> more=new ArrayList<Body>(); mHandler.postDelayed(new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { more.add(new Body("more+++"+i,100)); } mBodies.addAll(more); mRealPullRefreshView.loadMreFinish(); } }, 1500); } });
自定義刷新頭布局和動(dòng)畫
package com.example.apple.quickdemo.realview.show; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.example.apple.quickdemo.R; import com.example.apple.quickdemo.realview.view.RealPullRefreshView; /** * Created by apple on 2017/7/9. */ public class ImplOnPullShowViewListener implements RealPullRefreshView.OnPullShowViewListener { private TextView mTv; private ImageView mIv; private ObjectAnimator mAni; View mHeaderView; public ImplOnPullShowViewListener(RealPullRefreshView realPullRefreshView) { mHeaderView = realPullRefreshView.getRefreshHeaderView(); mTv = (TextView) mHeaderView.findViewById(R.id.tv); mIv = (ImageView) mHeaderView.findViewById(R.id.iv); mAni = ObjectAnimator.ofFloat(mIv, "rotation", -15, 15).setDuration(300); mAni.setRepeatCount(ValueAnimator.INFINITE); mAni.setRepeatMode(ValueAnimator.REVERSE); } @Override public void onPullDownRefreshState(int scrollY, int headviewHeight,int deltaY) { mTv.setText("下拉刷新"); float f = -((float) scrollY / (float) headviewHeight); Log.e("tag", f+ ""); Log.e("tag", -scrollY + "scrollY"); mIv.setScaleX(f); mIv.setScaleY(f); } @Override public void onReleaseRefreshState(int scrollY, int deltaY) { mTv.setText("松手刷新"); } @Override public void onRefreshingState() { mTv.setText("正在刷新"); mIv.setScaleX(1.0f); mIv.setScaleY(1.0f); mAni.start(); } @Override public void onDefaultState() { if (mAni.isRunning()){ mAni.end(); mIv.setRotation(0); } } }
源碼
package com.example.apple.quickdemo.realview.view; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.Nullable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.Scroller; import android.widget.Toast; import com.example.apple.quickdemo.R; import com.example.apple.quickdemo.realview.show.ImplOnPullShowViewListener; import static android.content.ContentValues.TAG; /** * Created by apple on 2017/7/7. * 下拉刷新 */ public class RealPullRefreshView extends LinearLayout { private int mTouchSlop; // 分別記錄上次滑動(dòng)的坐標(biāo) private int mLastX = 0; private int mLastY = 0; // 分別記錄上次滑動(dòng)的坐標(biāo)(onInterceptTouchEnvent) private int mLastXIntercept = 0; private int mLastYIntercept = 0; private Scroller mScroller; private VelocityTracker mVelocityTracker; private RecyclerView.Adapter mAdapter; public RecyclerView getRecyclerView() { return mRecyclerView; } private RecyclerView mRecyclerView; private int DEFAULT = 0; private final int PULL_DOWN_REFRESH = 1; private final int RELEASE_REFRESH = 2; private final int REFRESHING = 3; private final int LOAD_MORE = 4; private int STATE = DEFAULT; private int rfreshHeaderWidth; private int refreshHeadviewHeight; private OnPullListener mOnPullListener; private View mRefreshHeaderView; private RecyclerView.LayoutManager mLayoutManager; int refreshHeadviewId; public void setLayoutManager(RecyclerView.LayoutManager manager) { this.mLayoutManager = manager; mRecyclerView.setLayoutManager(mLayoutManager); } public void setAdapter(RecyclerView.Adapter adapter) { this.mAdapter = adapter; mRecyclerView.setAdapter(mAdapter); } public View getRefreshHeaderView() { return mRefreshHeaderView; } public void setOnPullShowViewListener(OnPullShowViewListener onPullShowViewListener) { mOnPullShowViewListener = onPullShowViewListener; } private OnPullShowViewListener mOnPullShowViewListener; public void setOnPullListener(OnPullListener onPullListener) { mOnPullListener = onPullListener; } public RealPullRefreshView(Context context) { super(context); initView(context); } public RealPullRefreshView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initAttrs(context, attrs); // ★★★★★一個(gè)坑initAttrs方法里的typedArray去獲取屬性時(shí),第一次獲取的屬性全是0,他會(huì)馬上重走一次構(gòu)造方法,再次獲取一次,才能獲得正確的值 // 如果第一次獲取的值為0,則不去initView if (refreshHeadviewId != 0) { initView(context); } } public RealPullRefreshView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); if (refreshHeadviewId != 0) { initView(context); } } private void initAttrs(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RealPullRefreshView); try { refreshHeadviewId = typedArray.getResourceId(R.styleable.RealPullRefreshView_refresh_header_view, 0); } finally { typedArray.recycle(); } } private void initView(Context context) { mScroller = new Scroller(getContext()); mVelocityTracker = VelocityTracker.obtain(); // 添加headerview // ★ ★ ★ ★ ★ 注意不要用這個(gè)方法inflate布局,會(huì)導(dǎo)致layout的所有屬性失效,height、width、margin // 原因見 http://blog.csdn.net/zhaokaiqiang1992/article/details/36006467 // ★ ★ ★ ★ ★ mRefreshHeaderView = mInflater.inflate(R.layout.headerview, null); mRefreshHeaderView = LayoutInflater.from(context).inflate(refreshHeadviewId, this, false); addView(mRefreshHeaderView); // } // 以下代碼主要是為了設(shè)置頭布局的marginTop值為-headerviewHeight // 注意必須等到一小會(huì)才會(huì)得到正確的頭布局寬高 postDelayed(new Runnable() { @Override public void run() { Log.e("q11", refreshHeadviewHeight + "qqqqqqqqqq " + mRefreshHeaderView.getHeight()); rfreshHeaderWidth = mRefreshHeaderView.getWidth(); refreshHeadviewHeight = mRefreshHeaderView.getHeight(); MarginLayoutParams lp = new LinearLayout.LayoutParams(rfreshHeaderWidth, refreshHeadviewHeight); lp.setMargins(0, -refreshHeadviewHeight, 0, 0); mRefreshHeaderView.setLayoutParams(lp); } }, 100); // 添加RecyclerView mRecyclerView = new RecyclerView(context); addView(mRecyclerView, LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); // 這里我提供了一個(gè)默認(rèn)的顯示效果,如果用戶不使用mRealPullRefreshView.setOnPullShowViewListener的話,會(huì)默認(rèn)使用這個(gè) // 用戶可以實(shí)現(xiàn)OnPullShowViewListener接口,去實(shí)現(xiàn)自己想要的顯示效果 mOnPullShowViewListener = new ImplOnPullShowViewListener(this); setLoadMore(); } private void setLoadMore() { // 當(dāng)目前的可見條目是所有數(shù)據(jù)的最后一個(gè)時(shí),開始加載新的數(shù)據(jù) mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int lastCompletelyVisibleItemPosition = -1; if (mLayoutManager instanceof LinearLayoutManager) { LinearLayoutManager manager = (LinearLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPosition(); } else if (mLayoutManager instanceof GridLayoutManager) { GridLayoutManager manager = (GridLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPosition(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) mLayoutManager; lastCompletelyVisibleItemPosition = manager.findLastVisibleItemPositions(new int[manager.getSpanCount()])[0]; } if (lastCompletelyVisibleItemPosition + 1 == mAdapter.getItemCount()) { if (mOnPullListener != null && STATE == DEFAULT) { STATE = LOAD_MORE; mOnPullListener.onLoadMore(); } } } }); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { boolean intercepted = false; int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { intercepted = false; /*if (STATE!=DEFAULT||STATE!=REFRESHING){ if (!mScroller.isFinished()) { mScroller.abortAnimation(); }}*/ break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; int firstCompletelyVisibleItemPosition = -1; if (mLayoutManager instanceof LinearLayoutManager) { LinearLayoutManager manager = (LinearLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPosition(); } else if (mLayoutManager instanceof GridLayoutManager) { GridLayoutManager manager = (GridLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPosition(); } else if (mLayoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) mLayoutManager; firstCompletelyVisibleItemPosition = manager.findFirstCompletelyVisibleItemPositions(new int[manager.getSpanCount()])[0]; } // ******************這里說(shuō)明什么規(guī)則下,攔截,其余代碼不要?jiǎng)恿?,其余代碼指的是處理滑動(dòng)沖突的代碼*************** if (firstCompletelyVisibleItemPosition == 0 && deltaY > 0 && Math.abs(deltaY) > Math.abs(deltaX)) {//拉倒最頂部,繼續(xù)往下拉,將拉出頭布局,要父布局?jǐn)r截 intercepted = true; } else if (getScrollY() < 0) {//表示頭布局已經(jīng)向下拉出來(lái),頭布局已經(jīng)顯示了,要父布局?jǐn)r截 intercepted = true; } else if (deltaY < 0) { intercepted = false;//不要父布局?jǐn)r截了 } else { intercepted = false;//不要父布局?jǐn)r截了 } // ******************什么規(guī)則下,攔截*************** break; } case MotionEvent.ACTION_UP: { intercepted = false; break; } default: break; } Log.d(TAG, "intercepted=" + intercepted); mLastX = x; mLastY = y; mLastXIntercept = x; mLastYIntercept = y; return intercepted; } /** * 下面不同布局,不同的滑動(dòng)需求 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; } case MotionEvent.ACTION_MOVE: { int deltaX = x - mLastX; int deltaY = y - mLastY; if (getScrollY() > 0) { //防止在正在刷新狀態(tài)下,上拉出空白 } else if (getScrollY() <= 0 && getScrollY() > -refreshHeadviewHeight * 5) { // 最多下拉到頭布局高度5倍的距離 scrollBy(0, -deltaY / 2); } if (getScrollY() > -refreshHeadviewHeight && STATE != REFRESHING) {//頭布局顯示不全時(shí),為下拉刷新PULL_DOWN_REFRESH狀態(tài) STATE = PULL_DOWN_REFRESH; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onPullDownRefreshState(getScrollY(), refreshHeadviewHeight, deltaY); } } if (getScrollY() < -refreshHeadviewHeight && STATE != REFRESHING) {//頭布局完全顯示時(shí),為釋放刷新RELEASE_REFRESH狀態(tài) STATE = RELEASE_REFRESH; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onReleaseRefreshState(getScrollY(), deltaY); } } break; } case MotionEvent.ACTION_UP: { final int scrollY = getScrollY(); //松手時(shí),根據(jù)所處的狀態(tài),讓布局滑動(dòng)到不同的地方,做不同的操作 switch (STATE) { case PULL_DOWN_REFRESH: STATE = DEFAULT; //頭布局沒(méi)有完全顯示,完全隱藏頭布局 smoothScrollBy(0, -scrollY); break; case RELEASE_REFRESH: STATE = REFRESHING; smoothScrollBy(0, -refreshHeadviewHeight - scrollY); if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onRefreshingState(); } if (mOnPullListener != null) { mOnPullListener.onRefresh(); } break; case REFRESHING: if (getScrollY() < -refreshHeadviewHeight) { smoothScrollBy(0, -refreshHeadviewHeight - scrollY); } else { smoothScrollBy(0, -scrollY); } break; } mVelocityTracker.clear(); break; } default: break; } mLastX = x; mLastY = y; return true; } /** * 當(dāng)用戶使用完下拉刷新回調(diào)時(shí),需要調(diào)用此方法,將頭不去隱藏,將STATE恢復(fù) */ public void refreshFinish() { smoothScrollBy(0, 0 - getScrollY()); getRecyclerView().getAdapter().notifyDataSetChanged(); STATE = DEFAULT; if (mOnPullShowViewListener != null) { mOnPullShowViewListener.onDefaultState(); } Toast.makeText(getContext(), "刷新成功!", Toast.LENGTH_SHORT).show(); } /** * 當(dāng)用戶使用完加載更多后回調(diào)時(shí),需要調(diào)用此方法,將STATE恢復(fù) */ public void loadMreFinish() { getRecyclerView().getAdapter().notifyDataSetChanged(); STATE = DEFAULT; Toast.makeText(getContext(), "加載成功了!", Toast.LENGTH_SHORT).show(); } /** * 在500毫秒內(nèi)平滑地滾動(dòng)多少像素點(diǎn) * * @param dx * @param dy */ private void smoothScrollBy(int dx, int dy) { mScroller.startScroll(0, getScrollY(), 0, dy, 500); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } /** * 釋放資源 */ @Override protected void onDetachedFromWindow() { mVelocityTracker.recycle(); super.onDetachedFromWindow(); } // *************** // ***************** /** * 回調(diào)接口 */ public interface OnPullListener { /** * 當(dāng)下拉刷新正在刷新時(shí),這時(shí)候可以去請(qǐng)求數(shù)據(jù),記得最后調(diào)用refreshFinish()復(fù)位 */ void onRefresh(); /** * 當(dāng)加載更多時(shí) */ void onLoadMore(); } /** * 回調(diào)接口,可以通過(guò)下面的回調(diào),自定義各種狀態(tài)下的顯示效果 * 可以根據(jù)下拉距離scrollY設(shè)計(jì)動(dòng)畫效果 */ public interface OnPullShowViewListener { /** * 當(dāng)處于下拉刷新時(shí),頭布局顯示效果 * * @param scrollY 下拉的距離 * @param headviewHeight 頭布局高度 * @param deltaY moveY-lastMoveY,正值為向下拉 */ void onPullDownRefreshState(int scrollY, int headviewHeight, int deltaY); /** * 當(dāng)處于松手刷新時(shí),頭布局顯示效果 * * @param scrollY 下拉的距離 * @param deltaY moveY-lastMoveY,正值為向下拉 */ void onReleaseRefreshState(int scrollY, int deltaY); /** * 正在刷新時(shí),頁(yè)面的顯示效果 */ void onRefreshingState(); /** * 默認(rèn)狀態(tài)時(shí),頁(yè)面顯示效果,主要是為了復(fù)位各種狀態(tài) */ void onDefaultState(); } }
以上是“Android如何實(shí)現(xiàn)RecyclerView下拉刷新效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)站名稱:Android如何實(shí)現(xiàn)RecyclerView下拉刷新效果
瀏覽路徑:http://www.muchs.cn/article2/ihejic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站導(dǎo)航、網(wǎng)站設(shè)計(jì)公司、自適應(yīng)網(wǎng)站、面包屑導(dǎo)航、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)