搞了這麼久的Android,多是我渠道比較閉塞,一直沒找到比較好用的下拉刷新,往上找的第三方比較知名的一些,都說有這樣那樣的瑕疵,一直用的SwipeRefreshLayout 作下拉加載,而後在RecycleView最後多加一個item,只要加載了這個item,就自動加載更多,要多low有多low,徹底沒有慾望去搞別人那種很炫酷的效果。java
附上原味效果圖(能夠經過繼承本代碼來定製,提供有相關方法和接口的,後續博客會說明並舉例):android
代碼區分了5中狀態供不一樣的應用場景使用,提供了方法可直接設置,也能夠各類狀態無縫切換:ide
下拉刷新兩種:可下拉、不可下拉(帶阻尼回彈)佈局
上拉加載三種:上拉加載、到底自動加載、不加載(帶阻尼回彈)post
源碼只有兩個文件,一個類(RefreshRelativeLayout)和一個佈局文件(refresh_relative_layout.xml),全部代碼均用的最原始的方式,並沒有其餘比較高深的第三方的東西,能夠直接導入使用動畫
經過設置回調接口監聽刷新和加載時間,至於數據,能夠經過getRecyclerView方法獲取RecyclerView,至於怎麼給RecyclerView加上數據操做,這裏我就很少廢話了,下面是一段調用代碼:this
//list是數據,adapter是適配器,這兩個東西我就不放出來了,你們都懂 RefreshRelativeLayout recyclerView = (RefreshRelativeLayout) findViewById(R.id.recyclerView); recyclerView.setOnRefreshListener(new RefreshRelativeLayout.OnRefreshListener() { @Override public void onRefresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { adapter.setData(list); recyclerView.setRefreshing(false); } }, 2000);//延遲兩秒操做 } }); recyclerView.setOnLoadListener(new RefreshRelativeLayout.OnLoadListener() { @Override public void onLoad() { new Handler().postDelayed(new Runnable() { @Override public void run() { adapter.addData(list); recyclerView.setLoading(false); } }, 2000);//延遲兩秒操做 } }); adapter.setData(list); recyclerView.getRecyclerView().setAdapter(adapter); }
總體構成是由一個父RelativeeLayout容器包裹三大部件(頭部RelativeLayout、RecyclerView、尾部RelativeLayout)構成;經過重寫父RelativeeLayout的dispatchTouchEvent方法監聽手指的觸摸事件,而後再根據手指移動的位置作各類狀態的切換處理,細節能夠看源碼,註釋都很是清晰了的,至少我自覺得是很清晰。spa
RecyclerView不用多說,頭部RelativeLayout和尾部RelativeLayout底下均包含了四個RelativeLayout用於表示四種不一樣的狀態,分別是:(1)下拉或上拉的時候未達到鬆開刷新加載的狀態(2)鬆開刷新或者加載(3)正在刷新或者加載(4)刷子、加載完成,這個狀態能夠設置延遲播放收起動畫的時間code
重寫UI的方法(後續會寫個博客介紹):xml
(1)、重寫一個refresh_relative_layout.xml,總體結構不能變,三大部件、4*2中狀態的RelativeLayout都不能動,可是能夠改變裏面的內容,以達到不一樣的狀態顯示不一樣的UI的效果。
(2)。繼承RefreshRelativeLayout並重寫裏面的一些方法,具體那些方法重寫能夠達到相關的功能,能夠看源碼,我經過分隔線進行了分類,另外提供了一個回調接口,用於回調當前手指拖動的距離,這樣方便製做下拉或者上拉時候的某種拖動動效(例如朋友圈的彩色圓圈,拖動的時候就轉,按着不動的時候就不轉)。
import android.content.Context; import android.graphics.Rect; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.view.animation.TranslateAnimation; import android.widget.RelativeLayout; import com.imxiaoyu.common.R; /** * 下拉刷新以及上啦加載更多 * Created by 她叫我小渝 on 2016/12/22. */ public class RefreshRelativeLayout extends RelativeLayout { /** * 常量 */ //pullDownType下拉類型的值;1-正常能夠下拉刷新 2-不能夠下拉刷新(有阻尼效果 ) public static final int PULL_DOWN_TYPE_REFRESH = 1; public static final int PULL_DOWN_TYPE_NOT_PULL = 2; //pullUpType上拉類型的值;1-能夠上拉加載 2-到了底部自動加載更多 3-不能夠上拉刷新(有阻尼效果) public static final int PULL_UP_TYPE_LOAD_PULL = 1; public static final int PULL_UP_TYPE_LOAD_AUTO = 2; public static final int PULL_UP_TYPE_NOT_PULL = 3; //pullDownState下拉狀態的值;1-下拉 2-鬆開刷新 3-正在刷新 4-刷新完成 public static final int PULL_DOWN_STATE_1 = 1; public static final int PULL_DOWN_STATE_2 = 2; public static final int PULL_DOWN_STATE_3 = 3; public static final int PULL_DOWN_STATE_4 = 4; //pullDownState上拉狀態的值;1-上拉 2-鬆開加載 3-正在加載 4-加載完成 public static final int PULL_UP_STATE_1 = 1; public static final int PULL_UP_STATE_2 = 2; public static final int PULL_UP_STATE_3 = 3; public static final int PULL_UP_STATE_4 = 4; /** * UI */ private RecyclerView recyclerView; private RelativeLayout rlyHead; private RelativeLayout rlyFoot; private RelativeLayout rlyHeadState1; private RelativeLayout rlyHeadState2; private RelativeLayout rlyHeadState3; private RelativeLayout rlyHeadState4; private RelativeLayout rlyFootState1; private RelativeLayout rlyFootState2; private RelativeLayout rlyFootState3; private RelativeLayout rlyFootState4; /** * 變量 */ private int pullDownType = PULL_DOWN_TYPE_REFRESH;//下拉類型; private int pullUpType = PULL_UP_TYPE_LOAD_PULL;//上拉類型; private int pullDownState = PULL_DOWN_STATE_1;//下拉狀態 private int pullUpState = PULL_UP_STATE_1;//上拉狀態 private boolean isRefreshing = false;//正在刷新 private boolean isLoading = false;//正在加載更多 private int afterRefreshDelayTime = 200;//刷新完成以後,延遲收起頭部視圖的時間 private boolean isTouching = false;//是否正在按壓着屏幕 // y方向上當前觸摸點的前一次記錄位置 private int previousY = 0; // y方向上的觸摸點的起始記錄位置 private int startY = 0; // y方向上的觸摸點當前記錄位置 private int currentY = 0; // y方向上兩次移動間移動的相對距離 private int deltaY = 0; // 用於記錄childView的初始位置 private Rect rectRlv = new Rect();//recyclerView的初始位置 private Rect rectRlyHead = new Rect();//頭部的初始位置 private Rect rectRlyFoot = new Rect();//尾部的初始位置 //水平移動的距離 private float moveHeight; /** * 接口 */ private OnRefreshListener onRefreshListener;//刷新回調 private OnLoadListener onLoadListener;//加載回調 private OnTouchHeightListener onTouchHeightListener;//拖動的高度監聽,正數是下拉,負數是上啦(不會爲0) public RefreshRelativeLayout(Context context) { this(context, null); } public RefreshRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RefreshRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); inflate(getContext(), getLayoutId(), this); initView(); } private void initView() { recyclerView = (RecyclerView) findViewById(R.id.rlv); rlyHead = getHeadLayout((RelativeLayout) findViewById(R.id.rly_refresh_head)); rlyFoot = getFootLayout((RelativeLayout) findViewById(R.id.rly_refresh_foot)); rlyHeadState1 = getHeadState1Layout((RelativeLayout) findViewById(R.id.rly_head_state_1)); rlyHeadState2 = getHeadState2Layout((RelativeLayout) findViewById(R.id.rly_head_state_2)); rlyHeadState3 = getHeadState3Layout((RelativeLayout) findViewById(R.id.rly_head_state_3)); rlyHeadState4 = getHeadState4Layout((RelativeLayout) findViewById(R.id.rly_head_state_4)); rlyFootState1 = getFootState1Layout((RelativeLayout) findViewById(R.id.rly_foot_state_1)); rlyFootState2 = getFootState2Layout((RelativeLayout) findViewById(R.id.rly_foot_state_2)); rlyFootState3 = getFootState3Layout((RelativeLayout) findViewById(R.id.rly_foot_state_3)); rlyFootState4 = getFootState4Layout((RelativeLayout) findViewById(R.id.rly_foot_state_4)); //初始化一下三大件的位置 initLayoutRect(); recyclerView.setOnScrollChangeListener(new OnScrollChangeListener() { @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { //四個斷定條件,1-不是正在加載 2-處於自動加載更多模式 3-列表已經到底部了 4-列表沒有在頂部(3-4一塊兒的意思是列表到底部了,且列表的數據必須大於一頁) if (!isLoading && pullUpType == PULL_UP_TYPE_LOAD_AUTO && !recyclerView.canScrollVertically(1) && recyclerView.canScrollVertically(-1)) { setPullUpState(PULL_UP_STATE_3);//到底部自動加載 } } }); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));//recyclerView默認是ListView同樣的列表 } //供外部調用的功能性方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓分隔線↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ /** * 返回RecyclerView,做爲數據操做使用 * @return */ public RecyclerView getRecyclerView() { return recyclerView; } /** * 設置下拉類型 1-正常能夠下拉刷新(默認) 2-不能夠下拉刷新(有阻尼效果 ) * ( 最好是在剛初始化的時候就調用) * * @param type */ public void setPullDownType(int type) { this.pullDownType = type; if (type == PULL_DOWN_TYPE_NOT_PULL) { rlyHead.setVisibility(GONE); } else { rlyHead.setVisibility(VISIBLE); } initLayoutRect(); } /** * 設置上啦類型 1-能夠下拉刷新(默認) 2-到了底部自動加載更多 3-不能夠上拉刷新(有阻尼效果) * ( 最好是在剛初始化的時候就調用) * * @param type */ public void setPullUpType(int type) { this.pullUpType = type; if (type == PULL_UP_TYPE_NOT_PULL) { rlyFoot.setVisibility(GONE); } else { rlyFoot.setVisibility(VISIBLE); } initLayoutRect(); } /** * 設置正在刷新或者刷新完成 * * @param bln */ public void setRefreshing(final boolean bln) { if (bln) { setPullDownState(PULL_DOWN_STATE_3); } else { setPullDownState(PULL_DOWN_STATE_4); } } /** * 設置正在刷新或者刷新完成 * * @param bln */ public void setLoading(final boolean bln) { if (bln) { setPullUpState(PULL_UP_STATE_3); } else { setPullUpState(PULL_UP_STATE_4); } } /** * 監聽下拉刷新狀態 * * @param listener */ public void setOnRefreshListener(OnRefreshListener listener) { this.onRefreshListener = listener; } /** * 監聽上啦或者自動加載狀態 * * @param listener */ public void setOnLoadListener(OnLoadListener listener) { this.onLoadListener = listener; } /** * 拖動的高度監聽,正數是下拉,負數是上啦(不會爲0,且只監聽拖動事件,按下和擡起均不回調) * * @param listener true-正在加載 false-加載完成 */ public void setOnLoadListener(OnTouchHeightListener listener) { this.onTouchHeightListener = listener; } /** * 設置刷新完成以後收起視圖的時間,毫秒單位 * * @param time */ public void setAfterRefreshDelayTime(int time) { this.afterRefreshDelayTime = time; } //供外部調用的功能性方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑分隔線↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ //如需自定義樣式,能夠繼承後重寫下方的方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓分隔線↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ /** * 使用的佈局 * * @return */ public int getLayoutId() { return R.layout.refresh_relative_layout; } /** * 頭部容器(若是須要特殊定製,能夠作些修改,但建議不要改動頭、尾的容器) * * @param rlyHead * @return */ public RelativeLayout getHeadLayout(RelativeLayout rlyHead) { return rlyHead; } /** * 下拉刷新的4種狀態的視圖 * * @param rlyHead * @return */ public RelativeLayout getHeadState1Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getHeadState2Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getHeadState3Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getHeadState4Layout(RelativeLayout rlyHead) { return rlyHead; } /** * 上拉刷新的4種狀態的視圖 * * @param rlyHead * @return */ public RelativeLayout getFootState1Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getFootState2Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getFootState3Layout(RelativeLayout rlyHead) { return rlyHead; } public RelativeLayout getFootState4Layout(RelativeLayout rlyHead) { return rlyHead; } /** * 尾部容器(若是須要特殊定製,能夠作些修改,但建議不要改動頭、尾的容器) * * @param rlyFoot * @return */ public RelativeLayout getFootLayout(RelativeLayout rlyFoot) { return rlyFoot; } /** * 回調動畫的動畫時長,能夠根據需求調節快一點慢一點 * @return */ public int getAnimTime(){ return 760; } //如需自定義樣式,能夠繼承後重寫上方的方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑分隔線↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ /** * 設置下拉的狀態 * * @param state 下拉狀態的值;1-下拉 2-鬆開刷新 3-正在刷新 4-刷新完成 */ private void setPullDownState(int state) { if (state < 1 || state > 4) { Log.e("錯誤","下拉狀態的值越界"); return; } pullDownState = state; if (onRefreshListener != null && state == PULL_DOWN_STATE_3) { //下拉刷新 onRefreshListener.onRefresh(); } changePullDownState(); } /** * 下拉狀態改變 */ private void changePullDownState() { isRefreshing = false; rlyHeadState1.setVisibility(INVISIBLE); rlyHeadState2.setVisibility(INVISIBLE); rlyHeadState3.setVisibility(INVISIBLE); rlyHeadState4.setVisibility(INVISIBLE); switch (pullDownState) { case PULL_DOWN_STATE_1: //下拉刷新 if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) { rlyHeadState1.setVisibility(VISIBLE); } break; case PULL_DOWN_STATE_2: //鬆開刷新 if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) { rlyHeadState2.setVisibility(VISIBLE); } break; case PULL_DOWN_STATE_3: //正在刷新 isRefreshing = true;//除了正在刷新以外,其餘狀態都不在刷新 if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) { rlyHeadState3.setVisibility(VISIBLE); } //不管當前再什麼位置,都跳到正在刷新的地方 moveViewAnimation(rlyHead, 0); moveViewAnimation(recyclerView, rlyHead.getHeight()); break; case PULL_DOWN_STATE_4: //刷新成功 if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) { rlyHeadState4.setVisibility(VISIBLE); } //不管當前再什麼位置,都要跳到初始的位置 postDelayed(new Runnable() { @Override public void run() { moveViewAnimation(rlyHead, -1 * rlyHead.getHeight()); moveViewAnimation(recyclerView, 0); } }, afterRefreshDelayTime); break; } } /** * 設置下拉的狀態 * * @param state 下拉狀態的值;1-下拉 2-鬆開刷新 3-正在刷新 4-刷新完成 */ private void setPullUpState(int state) { if (state < 1 || state > 4) { Log.e("錯誤","上拉狀態的值越界"); return; } pullUpState = state; if (onLoadListener != null && state == PULL_UP_STATE_3) { //下拉刷新 onLoadListener.onLoad(); } changePullUpState(); } /** * 下拉狀態改變 */ private void changePullUpState() { isLoading = false; rlyFootState1.setVisibility(INVISIBLE); rlyFootState2.setVisibility(INVISIBLE); rlyFootState3.setVisibility(INVISIBLE); rlyFootState4.setVisibility(INVISIBLE); switch (pullUpState) { case PULL_UP_STATE_1: //下拉刷新(自動加載更多的模式下禁止顯示) if (pullUpType != PULL_UP_TYPE_LOAD_AUTO && pullUpType != PULL_UP_TYPE_NOT_PULL) { rlyFootState1.setVisibility(VISIBLE); } break; case PULL_UP_STATE_2: //鬆開刷新(自動加載模式下禁止顯示) if (pullUpType != PULL_UP_TYPE_LOAD_AUTO && pullUpType != PULL_UP_TYPE_NOT_PULL) { rlyFootState2.setVisibility(VISIBLE); } break; case PULL_UP_STATE_3: //正在刷新 isLoading = true;//除了正在刷新以外,其餘狀態都不在刷新 if (pullUpType != PULL_UP_TYPE_NOT_PULL) { rlyFootState3.setVisibility(VISIBLE); } //不管當前再什麼位置,都跳到正在刷新的地方 moveViewAnimation(rlyFoot, getHeight() - rlyFoot.getHeight()); moveViewAnimation(recyclerView, rlyFoot.getHeight() * -1); break; case PULL_UP_STATE_4: //刷新成功 if (pullUpType != PULL_UP_TYPE_NOT_PULL) { rlyFootState4.setVisibility(VISIBLE); } //不管當前再什麼位置,都要跳到初始的位置 postDelayed(new Runnable() { @Override public void run() { moveViewAnimation(rlyFoot, getHeight()); moveViewAnimation(recyclerView, 0); } }, afterRefreshDelayTime); break; } } /** * 記錄三大控件的初始位置 */ private void recordLayoutRect() { // 記錄三大控件的初始位置 rectRlv.set(recyclerView.getLeft(), 0, recyclerView.getRight(), getHeight()); rectRlyHead.set(rlyHead.getLeft(), -1 * rlyHead.getHeight(), rlyHead.getRight(), 0); rectRlyFoot.set(rlyFoot.getLeft(), getHeight(), rlyFoot.getRight(), getHeight() + rlyFoot.getHeight()); } /** * 初始化三大控件的位置 */ private void initLayoutRect() { post(new Runnable() { @Override public void run() { //初始化位置 recyclerView.layout(recyclerView.getLeft(), 0, recyclerView.getRight(), getHeight()); rlyHead.layout(rlyHead.getLeft(), -1 * rlyHead.getHeight(), rlyHead.getRight(), 0); rlyFoot.layout(rlyFoot.getLeft(), getHeight(), rlyFoot.getRight(), getHeight() + rlyFoot.getHeight()); //綁定一下位置,以避免頁面重繪的時候位置發生錯亂 RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams(); params.topMargin=0; recyclerView.setLayoutParams(params); recyclerView.requestLayout(); params = (RelativeLayout.LayoutParams) rlyHead.getLayoutParams(); params.topMargin=-1*rlyHead.getHeight(); rlyHead.setLayoutParams(params); rlyHead.requestLayout(); params = (RelativeLayout.LayoutParams) rlyFoot.getLayoutParams(); params.topMargin=getHeight(); params.bottomMargin=getHeight()+rlyFoot.getHeight(); rlyFoot.setLayoutParams(params); rlyFoot.requestLayout(); //初始化狀態 setPullDownState(PULL_DOWN_STATE_1); setPullUpState(PULL_UP_STATE_1); } }); } @Override public boolean dispatchTouchEvent(MotionEvent event) { boolean isPullRecyclerView = false;//若是把頭拉下來了而後再推回上去的時候,必須把recyclerView的觸摸事件屏蔽掉,以避免列表上移 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isTouching = true; startY = (int) event.getY(); previousY = startY; moveHeight = 0; recordLayoutRect(); break; case MotionEvent.ACTION_MOVE: currentY = (int) event.getY(); deltaY = currentY - previousY; previousY = currentY; //斷定是否在頂部或者滑到了底部;第二類斷定是,正在刷新什麼的,禁止拖動列表 if ((!recyclerView.canScrollVertically(-1) && (currentY - startY) > 0) || (!recyclerView.canScrollVertically(1) && (currentY - startY) < 0) || (rlyHead.getTop() > rectRlyHead.top && (currentY - startY) > 0) || (rlyFoot.getTop() < rectRlyFoot.top && (currentY - startY) < 0)) { isPullRecyclerView = true;//下拉或者上啦的時候把recyclerView的觸摸事件屏蔽掉 //計算阻尼 float distance = currentY - startY; if (distance < 0) { distance *= -1; } float damping = 0.5f;//阻尼值 float height = getHeight(); if (height != 0) { if (distance > height) { damping = 0; } else { damping = (height - distance) / height; } } if (currentY - startY < 0) { damping = 1 - damping; } //阻力值限制再0.3-0.5之間,平滑過分 damping *= 0.25; damping += 0.25; moveHeight = moveHeight + (deltaY * damping); recyclerView.layout(rectRlv.left, (int) (rectRlv.top + moveHeight), rectRlv.right, (int) (rectRlv.bottom + moveHeight)); rlyHead.layout(rectRlyHead.left, (int) (rectRlyHead.top + moveHeight), rectRlyHead.right, (int) (rectRlyHead.bottom + moveHeight)); rlyFoot.layout(rectRlyFoot.left, (int) (rectRlyFoot.top + moveHeight), rectRlyFoot.right, (int) (rectRlyFoot.bottom + moveHeight)); //斷定是不是下拉動做並處於能夠下拉刷新狀態 if (pullDownType == PULL_DOWN_TYPE_REFRESH && moveHeight > 0) { if (moveHeight > rlyHead.getHeight()) { setPullDownState(PULL_DOWN_STATE_2); } else { setPullDownState(PULL_DOWN_STATE_1); } } //斷定是不是上拉動做而且處於上啦刷新狀態 if (pullUpType == PULL_UP_TYPE_LOAD_PULL && moveHeight < 0) { if (moveHeight * -1 > rlyFoot.getHeight()) { setPullUpState(PULL_UP_STATE_2); } else { setPullUpState(PULL_UP_STATE_1); } } //將當前拖動的高度回調 if (onTouchHeightListener != null) { //須要列表處於到頂的狀態 if (!recyclerView.canScrollVertically(-1) && moveHeight >= 1) { onTouchHeightListener.onTouchHeight((int) moveHeight); } //須要列表處於到底的狀態 if (!recyclerView.canScrollVertically(1) && moveHeight <= -1) { onTouchHeightListener.onTouchHeight((int) moveHeight); } } } break; case MotionEvent.ACTION_UP: isTouching = false; //斷定是不是下拉動做並處於能夠下拉刷新狀態 if (pullDownType == PULL_DOWN_TYPE_REFRESH && moveHeight > rlyHead.getHeight()) { //達到了鬆開刷新的條件 setPullDownState(PULL_DOWN_STATE_3); } else if (pullUpType == PULL_UP_TYPE_LOAD_PULL && moveHeight * -1 > rlyFoot.getHeight()) { //達到了鬆開加載的條件 setPullUpState(PULL_UP_STATE_3); } else { //開始回移動畫 moveViewAnimation(recyclerView, rectRlv.top); moveViewAnimation(rlyHead, rlyHead.getTop() + rlyHead.getHeight(), 0, rectRlyHead); moveViewAnimation(rlyFoot, rlyFoot.getTop() - rectRlyFoot.top, 0, rectRlyFoot); } //重置一些參數 startY = 0; currentY = 0; break; } if (isPullRecyclerView) { return isPullRecyclerView; } return super.dispatchTouchEvent(event); } /** * 控件移動動畫效果,從當前位置,移動到目標位置 * * @param view 待移動的控件 * @param stopY 移動到的目標位置 */ private void moveViewAnimation(View view, int stopY) { moveViewAnimation(view, view.getTop() - stopY, 0, new Rect(view.getLeft(), stopY, view.getRight(), stopY + view.getHeight())); } /** * 控件移動動畫效果 * * @param view 待移動的控件 * @param startY 移動開始的位置(相對控件自己) * @param stopY 移動結束的位置(相對控件自己) * @param top 動畫結束後的位置,即設置控件自己Top的位置 * @param bottom 動畫結束後的位置,即設置控件自己bottom的位置 */ private void moveViewAnimation(View view, int startY, int stopY, int top, int bottom) { moveViewAnimation(view, startY, stopY, new Rect(view.getLeft(), top, view.getRight(), bottom)); } /** * 控件移動動畫效果 * * @param view 待移動的控件 * @param startY 移動開始的位置(相對控件自己) * @param stopY 移動結束的位置(相對控件自己) * @param rect 動畫結束後的位置,即設置控件自己的位置 */ private void moveViewAnimation(final View view, int startY, int stopY, Rect rect) { if (isTouching) { return;//若是還按壓着屏幕,則不播聽任何動畫 } TranslateAnimation animation = new TranslateAnimation(0.0f, 0.0f, startY, stopY); animation.setDuration(getAnimTime());//動畫的持續時間 animation.setFillAfter(true); //設置阻尼動畫效果 animation.setInterpolator(new DampInterpolator()); view.setAnimation(animation); view.layout(rect.left, rect.top, rect.right, rect.bottom); recordLayoutRect();//記錄位置,用於重繪 } public class DampInterpolator implements Interpolator { @Override public float getInterpolation(float input) { //沒看過源碼,猜想是input是時間(0-1),返回值應該是進度(0-1) //先快後慢,爲了更快更慢的效果,多乘了幾回,如今這個效果比較滿意 return 1 - (1 - input) * (1 - input) * (1 - input) * (1 - input); } } public void requestLayout() { if (isRefreshing||isLoading) { //刷新recyclerView裏面的數據的時候,會調用requestLayout()方法刷新控件,會導刷新刷新完數據以後沒有三大部件顯示動畫就復位了,因此這裏屏蔽掉requestLayout方法換成invalidate()刷新頁面 invalidate(); } else { super.requestLayout(); } } /** * 接口 */ public interface OnRefreshListener { void onRefresh(); } public interface OnLoadListener { void onLoad(); } public interface OnTouchHeightListener { void onTouchHeight(int num); } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rly_bg" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/rly_refresh_head" android:layout_width="match_parent" android:layout_height="60dp"> <RelativeLayout android:id="@+id/rly_head_state_1" android:layout_width="match_parent" android:layout_height="match_parent"> <!--下拉但還未到頭部的高度--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="下拉刷新" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_head_state_2" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--下拉且超過了頭部的高度,可鬆開刷新--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="鬆開刷新" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_head_state_3" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--正在刷新--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="正在刷新" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_head_state_4" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--刷新成功--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="刷新成功" /> </RelativeLayout> </RelativeLayout> <android.support.v7.widget.RecyclerView android:id="@+id/rlv" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:scrollbars="none" /> <RelativeLayout android:id="@+id/rly_refresh_foot" android:layout_width="match_parent" android:layout_height="60dp"> <RelativeLayout android:id="@+id/rly_foot_state_1" android:layout_width="match_parent" android:layout_height="match_parent"> <!--下拉但還未到頭部的高度--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="上拉加載" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_foot_state_2" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--下拉且超過了頭部的高度,可鬆開刷新--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="鬆開加載" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_foot_state_3" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--正在加載--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="正在加載" /> </RelativeLayout> <RelativeLayout android:id="@+id/rly_foot_state_4" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible"> <!--刷新成功--> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="加載成功" /> </RelativeLayout> </RelativeLayout> </RelativeLayout>
最後,有些狀態考慮的不必定周全,若是代碼在使用的過程當中出現了某些問題,能夠在下方評論,也能夠給我發郵件:652698011@qq.com