TwinklingRefreshLayout v1.04 版精心重構(gòu),優(yōu)化 UI洋机、刷新及越界動(dòng)畫效果坠宴,修復(fù)眾多 bug,完美發(fā)布槐秧!
TwinklingRefreshLayout延伸了Google的SwipeRefreshLayout的思想,不在列表控件上動(dòng)刀,而是使用一個(gè)ViewGroup來包含列表控件,以保持其較低的耦合性和較高的通用性啄踊。其主要特性有:
- 支持RecyclerView、ScrollView刁标、AbsListView系列(ListView颠通、GridView)、WebView以及其它可以獲取到scrollY的控件
- 支持加載更多
- 默認(rèn)支持 越界回彈膀懈,隨手勢(shì)速度有不同的效果
- 可開啟沒有刷新控件的純凈越界回彈模式
- setOnRefreshListener中擁有大量可以回調(diào)的方法
- 將Header和Footer抽象成了接口,并回調(diào)了滑動(dòng)過程中的系數(shù),方便實(shí)現(xiàn)個(gè)性化的Header和Footer
Demo
You can download the Video for more details.
- Music - ListView - FixedHeader
- Food - RecyclerView - PureScrollMode
- Science - GridView - SinaHeader
- Photo - RecyclerView - BezierLayout
- Story - ScrollView - GoogleDotView
- Dribbble - WebView - FloatRefresh
使用方法
1.添加gradle依賴
將libray模塊復(fù)制到項(xiàng)目中,或者直接在build.gradle中依賴:
compile 'com.lcodecorex:tkrefreshlayout:1.0.4'
2.在xml中添加TwinklingRefreshLayout
<?xml version="1.0" encoding="utf-8"?>
<com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:tr_wave_height="180dp"
app:tr_head_height="100dp">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:background="#fff" />
</com.lcodecore.library.TwinklingRefreshLayout>
Android系統(tǒng)為了跟iOS不一樣顿锰,當(dāng)界面OverScroll的時(shí)候會(huì)顯示一個(gè)陰影。為了達(dá)到更好的顯示效果启搂,最好禁用系統(tǒng)的overScroll硼控,如上給RecyclerView添加android:overScrollMode="never"
。
3.在Activity或者Fragment中配置
TwinklingRefreshLayout不會(huì)自動(dòng)結(jié)束刷新或者加載更多胳赌,需要手動(dòng)控制
refreshLayout.setOnRefreshListener(new RefreshListenerAdapter(){
@Override
public void onRefresh(final TwinklingRefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
refreshLayout.finishRefreshing();
}
},2000);
}
@Override
public void onLoadMore(final TwinklingRefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
refreshLayout.finishLoadmore();
}
},2000);
}
});
}
使用finishRefreshing()方法結(jié)束刷新牢撼,finishLoadmore()方法結(jié)束加載更多。此處OnRefreshListener還有其它方法疑苫,可以選擇需要的來重寫熏版。
如果你想進(jìn)入到界面的時(shí)候主動(dòng)調(diào)用下刷新,可以調(diào)用startRefresh()/startLoadmore()方法捍掺。
setWaveHeight撼短、setHeaderHeight、setBottomHeight挺勿、setOverScrollHeight
- setWaveHeight 設(shè)置頭部可拉伸的最大高度曲横。
- setHeaderHeight 頭部固定高度(在此高度上顯示刷新狀態(tài))
- setBottomHeight 底部高度
- setOverScrollHeight 設(shè)置最大的越界高度
setEnableRefresh、setEnableLoadmore
靈活的設(shè)置是否禁用上下拉不瓶。
setHeaderView(IHeaderView headerView)禾嫉、setBottomView(IBottomView bottomView)
設(shè)置頭部/底部個(gè)性化刷新效果灾杰,頭部需要實(shí)現(xiàn)IHeaderView,底部需要實(shí)現(xiàn)IBottomView夭织。
setEnableOverScroll
是否允許越界回彈吭露。
setOverScrollTopShow吠撮、setOverScrollBottomShow尊惰、setOverScrollRefreshShow
是否允許在越界的時(shí)候顯示刷新控件,默認(rèn)是允許的泥兰,也就是Fling越界的時(shí)候Header或Footer照常顯示弄屡,反之就是不顯示;可能有特殊的情況鞋诗,刷新控件會(huì)影響顯示體驗(yàn)才設(shè)立了這個(gè)狀態(tài)膀捷。
setPureScrollModeOn()
開啟純凈的越界回彈模式,也就是所有刷新相關(guān)的View都不顯示削彬,只顯示越界回彈效果
setAutoLoadMore
是否在底部越界的時(shí)候自動(dòng)切換到加載更多模式
addFixedExHeader
添加一個(gè)固定在頂部的Header(效果還需要優(yōu)化)
startRefresh全庸、startLoadMore、finishRefreshing融痛、finishLoadmore
setFloatRefresh(boolean)
支持切換到像SwipeRefreshLayout一樣的懸浮刷新模式了壶笼。
4.擴(kuò)展屬性
- tr_wave_height 頭部拉伸允許的最大高度
- tr_head_height 頭部高度
- tr_bottom_height 底部高度
- tr_overscroll_height 允許越界的最大高度
- tr_enable_loadmore 是否允許加載更多,默認(rèn)為true
- tr_pureScrollMode_on 是否開啟純凈的越界回彈模式
- tr_overscroll_top_show - 否允許頂部越界時(shí)顯示頂部View
- tr_overscroll_bottom_show 是否允許底部越界時(shí)顯示底部View
- tr_enable_overscroll 是否允許越界回彈
其它說明
1.默認(rèn)支持越界回彈,并可以隨手勢(shì)越界不同的高度
這一點(diǎn)很多類似SwipeRefreshLayout的刷新控件都沒有做到(包括SwipeRefreshLayout),因?yàn)闆]有攔截下來的時(shí)間會(huì)傳遞給列表控件雁刷,而列表控件的滾動(dòng)狀態(tài)很難獲取覆劈。解決方案就是給列表控件設(shè)置了OnTouchListener并把事件交給GestureDetector處理,然后在列表控件的OnScrollListener中監(jiān)聽View是否滾動(dòng)到了頂部(沒有OnScrollListener的則采用延時(shí)監(jiān)聽策略)。
2.setOnRefreshListener大量可以回調(diào)的方法
- onPullingDown(TwinklingRefreshLayout refreshLayout, float fraction) 正在下拉的過程
- onPullingUp(TwinklingRefreshLayout refreshLayout, float fraction) 正在上拉的過程
- onPullDownReleasing(TwinklingRefreshLayout refreshLayout, float fraction) 下拉釋放過程
- onPullUpReleasing(TwinklingRefreshLayout refreshLayout, float fraction) 上拉釋放過程
- onRefresh(TwinklingRefreshLayout refreshLayout) 正在刷新
- onLoadMore(TwinklingRefreshLayout refreshLayout) 正在加載更多
其中fraction表示當(dāng)前下拉的距離與Header高度的比值(或者當(dāng)前上拉距離與Footer高度的比值)沛励。
3.Header和Footer
BezierLayout(pic 4)
- setWaveColor
- setRippleColor
GoogleDotView(pic 5)
SinaRefreshView(pic 3)
- setArrowResource
- setTextColor
- setPullDownStr
- setReleaseRefreshStr
- setRefreshingStr
ProgressLayout(SwipeRefreshLayout pic 6)
- setProgressBackgroundColorSchemeResource(@ColorRes int colorRes)
- setProgressBackgroundColorSchemeColor(@ColorInt int color)
- setColorSchemeResources(@ColorRes int... colorResIds)
Footer
BottomProgressView(pic 2)
- setNormalColor(@ColorInt int color)
- setAnimatingColor(@ColorInt int color)
LoadingView(pic 3)
更多動(dòng)效可以參考AVLoadingIndicatorView庫责语。
3.實(shí)現(xiàn)個(gè)性化的Header和Footer
相關(guān)接口分別為IHeaderView和IBottomView,代碼如下:
public interface IHeaderView {
View getView();
void onPullingDown(float fraction,float maxHeadHeight,float headHeight);
void onPullReleasing(float fraction,float maxHeadHeight,float headHeight);
void startAnim(float maxHeadHeight,float headHeight);
void reset();
}
其中g(shù)etView()方法用于在TwinklingRefreshLayout中獲取到實(shí)際的Header,因此不能返回null。
實(shí)現(xiàn)像新浪微博那樣的刷新效果(有部分修改,具體請(qǐng)看源碼),實(shí)現(xiàn)代碼如下:
1.首先定義SinaRefreshHeader繼承自FrameLayout并實(shí)現(xiàn)IHeaderView方法
2.getView()方法中返回this
3.在onAttachedToWindow()或者構(gòu)造函數(shù)方法中獲取一下需要用到的布局
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (rootView == null) {
rootView = View.inflate(getContext(), R.layout.view_sinaheader, null);
refreshArrow = (ImageView) rootView.findViewById(R.id.iv_arrow);
refreshTextView = (TextView) rootView.findViewById(R.id.tv);
loadingView = (ImageView) rootView.findViewById(R.id.iv_loading);
addView(rootView);
}
}
4.實(shí)現(xiàn)其它方法
@Override
public void onPullingDown(float fraction, float maxHeadHeight, float headHeight) {
if (fraction < 1f) refreshTextView.setText(pullDownStr);
if (fraction > 1f) refreshTextView.setText(releaseRefreshStr);
refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180);
}
@Override
public void onPullReleasing(float fraction, float maxHeadHeight, float headHeight) {
if (fraction < 1f) {
refreshTextView.setText(pullDownStr);
refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180);
if (refreshArrow.getVisibility() == GONE) {
refreshArrow.setVisibility(VISIBLE);
loadingView.setVisibility(GONE);
}
}
}
@Override
public void startAnim(float maxHeadHeight, float headHeight) {
refreshTextView.setText(refreshingStr);
refreshArrow.setVisibility(GONE);
loadingView.setVisibility(VISIBLE);
}
5.布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow"/>
<ImageView
android:id="@+id/iv_loading"
android:visibility="gone"
android:layout_width="34dp"
android:layout_height="34dp"
android:src="@drawable/anim_loading_view"/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:textSize="16sp"
android:text="下拉刷新"/>
</LinearLayout>
注意fraction的使用,比如上面的代碼refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180)
目派,fraction * headHeight
表示當(dāng)前頭部滑動(dòng)的距離坤候,然后算出它和最大高度的比例,然后乘以180企蹭,可以使得在滑動(dòng)到最大距離時(shí)Arrow恰好能旋轉(zhuǎn)180度白筹。
onPullingDown/onPullingUp表示正在下拉/正在上拉的過程。
onPullReleasing表示向上拉/下拉釋放時(shí)回調(diào)的狀態(tài)练对。
startAnim則是在onRefresh/onLoadMore之后才會(huì)回調(diào)的過程(此處是顯示了加載中的小菊花)
如上所示遍蟋,輕而易舉就可以實(shí)現(xiàn)一個(gè)個(gè)性化的Header或者Footer。(更簡(jiǎn)單的實(shí)現(xiàn)請(qǐng)參考Demo中的 TextHeaderView(圖四))螟凭。
TODO
- 制作一個(gè)star相關(guān)的動(dòng)效
- CoordinateLayout及NestedScroll支持
- 帶視差效果的Header
更新日志
v1.04
新增功能
- 第二次重構(gòu)完成,將核心邏輯拆分為RefreshProcessor虚青、AnimProcessor、OverScrollProcessor螺男、CoProcessor
- 優(yōu)化越界策越棒厘,手勢(shì)決定越界高度
- 優(yōu)化界面流暢度
- 添加類似SwipeRefreshLayout的懸浮刷新功能(ProgressLayout)
- 滑到底部自動(dòng)加載更多or回彈可選纵穿,默認(rèn)為回彈
- 允許在結(jié)束刷新之前執(zhí)行一個(gè)動(dòng)效:IHeadView.onFinish(animEndListener)
- 新增支持Header(Beta)
- 優(yōu)化BezierLayout、SinaRefreshLayout等的顯示并增加調(diào)節(jié)屬性
- 新增支持設(shè)置是否允許OverScroll
fixed bugs
- 修復(fù)刷新或加載更多時(shí)奢人,列表item沒有鋪滿列表控件谓媒,滑動(dòng)無效的問題
- 添加主動(dòng)刷新/加載更多的方法:startRefresh(),startLoadMore()
- 修復(fù)頂部和底部越界高度不一致的問題
- 修復(fù)WebView在底部fling時(shí)不能越界的問題
- 動(dòng)畫執(zhí)行時(shí)間與高度相關(guān),動(dòng)效更加柔和
v1.03
- 擴(kuò)展了更多的屬性
- 修復(fù)Fragment回收導(dǎo)致的空指針異常問題
- 加入x方向判斷,減小了滑動(dòng)沖突
- 優(yōu)化加載更多列表顯示問題
- 可以靈活的設(shè)置是否禁用上下拉
- 修復(fù)GridView滑動(dòng)過程中出現(xiàn)的白條問題
- Demo中添加輪播條展示
v1.02
- 修復(fù)加載更多列表控件的顯示問題
v1.01
- 支持了RecyclerView何乎、ScrollView句惯、AbsListView、WebView
- 支持越界回彈
- 支持個(gè)性化Header支救、Footer
ps:如有任何問題或者是建議抢野,可以郵箱聯(lián)系我!(lcodecore@163.com)