android教你打造獨(dú)一無二的上拉下拉刷新加載框架
其實(shí)早在去年七月,群里小伙伴就有讓我共享這個(gè)。但我當(dāng)時(shí)絕的技術(shù)不純熟。代碼有bug什么的。沒有寫出來?,F(xiàn)在感覺整理的差不多了。就寫出來讓大家看看,有問題一起討論解決。
說到刷新加載,我們第一個(gè)想到啥,對(duì)了就是swiperefreshlayout,還有什么SuperSwiperefreshlayout,XRecyclerView等等。反正老多了,我還是之前那句話,不管用什么,我們需要知道他的原理。
打造框架開始
對(duì)于刷新加載的實(shí)現(xiàn),你們第一個(gè)想到的是什么?是用swiperefresh然后在recyclerview底部加一個(gè)不同type?還是用事件攔截呢?那必須是事件攔截啊,雖然現(xiàn)在swiperefreshlayout很火,基本很多app都能看到他。但是遇到那種坑比公司說刷新要用自己公司logo你也沒轍。對(duì)把。。好了,感覺得罪了好多公司,不管他,我們繼續(xù)。
我們先看下我們的效果圖:
老鐵,沒毛病。下面我介紹如何實(shí)現(xiàn)的。
下拉刷新
首先我們給出如下幾個(gè)參數(shù),后面要用:
private NestedScrollingParentHelper helper = null; private boolean IsRefresh = true; private boolean IsLoad = true; //滑動(dòng)的總距離 private int totalY = 0; private LinearLayout headerLayout = null; private MyRecyclerView myRecyclerView = null; private LinearLayout footerLayout = null;
既然是刷新,我們的滾動(dòng)肯定是在父view之前的。所以我們需要在onNestedPreScroll這個(gè)方法里面寫上我們所需要改動(dòng)的x,y值。
我們需要用父view去攔截它。
我們需要判斷dy的值是否大于0,因?yàn)榇笥?是刷新操作,小于0是加載操作。然后我們需要判斷recyclerview是否是縱向的而不是橫向的。
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { if (IsRefresh) { if (dy > 0) { if (myRecyclerView.isOrientation(0)) { totalY += dy; if ((totalY / 2) <= 0) { scrollTo(0, totalY / 2); consumed[1] = dy; } else { scrollTo(0, 0); consumed[1] = 0; } } return; } }
上拉加載
上面我也說了onNestedPreScroll這個(gè)方法中判斷dy<0才是加載操作。所以綜上所述,代碼變成了這樣:
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { if (totalY < 0 && myRecyclerView.isOrientation(0) || totalY > 0 && myRecyclerView.isOrientation(1)) { isfling = true; } if (IsRefresh) { if (dy > 0) { if (myRecyclerView.isOrientation(0)) { totalY += dy; if ((totalY / 2) <= 0) { scrollTo(0, totalY / 2); consumed[1] = dy; } else { scrollTo(0, 0); consumed[1] = 0; } } return; } } if (IsLoad) { if (dy < 0) { if (myRecyclerView.isOrientation(1)) { totalY += dy; if ((totalY / 2) >= 0) { scrollTo(0, totalY / 2); consumed[1] = dy; } else { scrollTo(0, 0); consumed[1] = 0; } } return; } } }
最后我們需要在子view滑動(dòng)結(jié)束后,實(shí)行如下操作:
//子view滑動(dòng)結(jié)束調(diào)用 //dyUnconsumed < 0 向下滾 //dyUnconsumed > 0 向上滾 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { if (dyUnconsumed != 0) { totalY += dyUnconsumed; scrollTo(0, totalY / 2); } }
其實(shí)最主要的兩個(gè)方法已經(jīng)解決了,其他到?jīng)]什么了,這邊,我把nestedscrolling的8個(gè)接口的功能和自定義recyclerview放出來。已變大家參考。希望大家都能實(shí)現(xiàn)自己的刷新加載。告別swiperefreshlayout。
添加header和footer
這里我們參考listview自帶的addheaderview和addfooterview。代碼如下:
public void addHeaderView(View headerView, int headerHeight) { this.headerLayout.removeAllViews(); this.headerLayout.addView(headerView); LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, headerHeight); layoutParams.topMargin = -headerHeight; this.headerLayout.setLayoutParams(layoutParams); } public void addFooterView(View footerView, int footerHeight) { this.footerLayout.removeAllViews(); this.footerLayout.addView(footerView); this.footerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, footerHeight)); }
幾個(gè)接口的實(shí)現(xiàn)
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return true; } public void onNestedScrollAccepted(View child, View target, int axes) { helper.onNestedScrollAccepted(child, target, axes); } //父view攔截子view的滾動(dòng) public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { if (totalY < 0 && myRecyclerView.isOrientation(0) || totalY > 0 && myRecyclerView.isOrientation(1)) { isfling = true; } if (IsRefresh) { if (dy > 0) { if (myRecyclerView.isOrientation(0)) { totalY += dy; if ((totalY / 2) <= 0) { scrollTo(0, totalY / 2); consumed[1] = dy; } else { scrollTo(0, 0); consumed[1] = 0; } } return; } } if (IsLoad) { if (dy < 0) { if (myRecyclerView.isOrientation(1)) { totalY += dy; if ((totalY / 2) >= 0) { scrollTo(0, totalY / 2); consumed[1] = dy; } else { scrollTo(0, 0); consumed[1] = 0; } } return; } } } //子view滑動(dòng)結(jié)束調(diào)用 //dyUnconsumed < 0 向下滾 //dyUnconsumed > 0 向上滾 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { if (dyUnconsumed != 0) { totalY += dyUnconsumed; scrollTo(0, totalY / 2); } } public void onStopNestedScroll(View child) { helper.onStopNestedScroll(child); if (onTouchUpListener != null) { isfling = false; onTouchUpListener.touchUp(); } } public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { return isfling; } public boolean onNestedPreFling(View target, float velocityX, float velocityY) { return isfling; } public int getNestedScrollAxes() { return helper.getNestedScrollAxes(); }
自定義recyclerview
既然是自己寫的刷新加載框架,總不能還有自定義layout中在放個(gè)recyclerview。多麻煩,自定義一個(gè),直接放在里面,然后分別放個(gè)header和footer 就沒必要每次有頁面用到刷新都要寫一個(gè)布局。3個(gè)布局解決整個(gè)項(xiàng)目的刷新和加載。話不多說,代碼如下:
private class MyRecyclerView extends RecyclerView { private StaggeredGridLayoutManager staggeredGridLayoutManager = null; private LinearLayoutManager linearLayoutManager = null; private GridLayoutManager gridLayoutManager = null; private boolean isScrollLoad = false; private boolean isScrollRefresh = false; public MyRecyclerView(Context context) { super(context); setVerticalFadingEdgeEnabled(false); setHorizontalFadingEdgeEnabled(false); setVerticalScrollBarEnabled(false); setHorizontalScrollBarEnabled(false); setOverScrollMode(OVER_SCROLL_NEVER); setItemAnimator(new DefaultItemAnimator()); } private void setMyLayoutManager(LayoutManager layoutManager) { if (layoutManager instanceof StaggeredGridLayoutManager) { staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; } else if (layoutManager instanceof GridLayoutManager) { gridLayoutManager = (GridLayoutManager) layoutManager; } else if (layoutManager instanceof LinearLayoutManager) { linearLayoutManager = (LinearLayoutManager) layoutManager; } setLayoutManager(layoutManager); if (!isVertical()) { throw new NullPointerException("vertical!"); } } private boolean isOrientation(int orientation) {//orientation,0代表向下,1代表向上 if (orientation == 0) return isCanPullDown(); else if (orientation == 1) return isCanPullUp(); return false; } private boolean isCanPullDown() { return !canScrollVertically(-1); } private boolean isCanPullUp() { return !canScrollVertically(1); } // private int scrollLoad() { // int lastItem = 0; // int itemCount = 0; // int spanCount = 1; // if (staggeredGridLayoutManager != null) { // lastItem = staggeredGridLayoutManager.findLastVisibleItemPositions(null)[0]; // itemCount = staggeredGridLayoutManager.getItemCount(); // spanCount = staggeredGridLayoutManager.getSpanCount(); // } else if (linearLayoutManager != null) { // lastItem = linearLayoutManager.findLastVisibleItemPosition(); // itemCount = linearLayoutManager.getItemCount(); // spanCount = 1; // } else if (gridLayoutManager != null) { // lastItem = gridLayoutManager.findLastVisibleItemPosition(); // itemCount = gridLayoutManager.getItemCount(); // spanCount = gridLayoutManager.getSpanCount(); // } // return ((itemCount - 1) / spanCount + 1) - (lastItem / spanCount + 1); // } private boolean isVertical() { if (staggeredGridLayoutManager != null) return staggeredGridLayoutManager.getOrientation() == StaggeredGridLayoutManager.VERTICAL; else if (linearLayoutManager != null) return linearLayoutManager.getOrientation() == LinearLayoutManager.VERTICAL; else if (gridLayoutManager != null) return gridLayoutManager.getOrientation() == GridLayoutManager.VERTICAL; return false; } // public void onScrolled(int dx, int dy) { // if (dy > 0 && !isScrollLoad) { // if (oLior != null) { // onScrollListener.scrollLoad(sc```````` ` `` llLoad());//傳遞滾動(dòng)到倒數(shù)第幾行 // } // } // } }
這樣我們變實(shí)現(xiàn)了自己的刷新加載框架,代碼我已上傳:SWPullRecyclerLayout_jb51.rar
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
一文帶你了解Android系統(tǒng)的啟動(dòng)流程
Android系統(tǒng)的啟動(dòng)是一個(gè)復(fù)雜的過程,涉及到多個(gè)階段和組件,所以本文將給大家詳細(xì)的介紹一下Android系統(tǒng)的啟動(dòng)流程,文中也有圖片和代碼示例的講解,需要的朋友可以參考下2023-09-09Flutter添加頁面過渡動(dòng)畫實(shí)現(xiàn)步驟
這篇文章主要為大家介紹了Flutter添加頁面過渡動(dòng)畫,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Android編程開發(fā)之在Canvas中利用Path繪制基本圖形(圓形,矩形,橢圓,三角形等)
這篇文章主要介紹了Android編程開發(fā)之在Canvas中利用Path繪制基本圖形的方法,涉及Android基本的圖形繪制技巧,結(jié)合實(shí)例分析了繪制圓形,矩形,橢圓,三角形等基本圖形的實(shí)現(xiàn)方法,需要的朋友可以參考下2016-01-01android輕量級(jí)無侵入式管理數(shù)據(jù)庫自動(dòng)升級(jí)組件
這篇文章主要為大家介紹了android輕量級(jí)無侵入式管理數(shù)據(jù)庫自動(dòng)升級(jí)組件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02android實(shí)現(xiàn)定時(shí)拍照并發(fā)送微博功能
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)定時(shí)拍照并發(fā)送微博功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android編程實(shí)現(xiàn)獲取圖片資源的四種方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)獲取圖片資源的四種方法,分別針對(duì)圖片所在目錄位置分析了Android獲取圖片資源的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android控件Tween動(dòng)畫(補(bǔ)間動(dòng)畫)實(shí)現(xiàn)方法示例
這篇文章主要介紹了Android控件Tween動(dòng)畫(補(bǔ)間動(dòng)畫)實(shí)現(xiàn)方法,結(jié)合具體實(shí)例形式分析了Android補(bǔ)間動(dòng)畫的原理、功能實(shí)現(xiàn)與布局相關(guān)操作技巧,需要的朋友可以參考下2017-08-08android中RecycleView添加下滑到底部的監(jiān)聽示例
本篇文章主要介紹了android中RecycleView添加下滑到底部的監(jiān)聽示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03詳談Android中onTouch與onClick事件的關(guān)系(必看)
下面小編就為大家?guī)硪黄斦凙ndroid中onTouch與onClick事件的關(guān)系(必看)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03