Android如何實(shí)現(xiàn)RecyclerView下拉刷新效果

這篇文章主要介紹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)

成都網(wǎng)頁(yè)設(shè)計(jì)公司