??之前我們介紹過RecyclerView和SwipeRefreshLayout的交互工作模式递递,現(xiàn)在楼镐,讓我們擴(kuò)展一下SwipeRefreshLayout的功能讓它能支持上拉加載癞志,并且能和原生的SwipeRefreshLayout效果切換。
1.功能
支持RecyclerView和ListView下拉刷新和上拉加載
支持主動刷新
下拉刷新能使用SwipeRefreshLayout的原生樣式或自定義的延展樣式
可自定義ViewGroup定制刷新樣式和加載樣式
2.說明
??總體來說框产,因?yàn)镾wipeRefreshLayout本身就支持RecyclerView和ListView的下拉刷新功能凄杯,并且一定程度上支持嵌套滑動(對ListView無法支持,當(dāng)RecyclerView開啟嵌套滑動時(shí)可以支持)秉宿,所以我們這個(gè)自定義控件繼承SwipeRefreshLayout實(shí)現(xiàn)是相當(dāng)方便的戒突,但我們還要考慮到以下這些情況,這也是我們這個(gè)自定義控件的主要思路蘸鲸。
要實(shí)現(xiàn)延展樣式,需要在RecyclerView和ListView中加入headView和footView動態(tài)改變它們的高度窿锉,當(dāng)控件處于最頂端繼續(xù)下拉時(shí)酌摇,讓headView的height隨手勢的滑動而變化膝舅,當(dāng)控件處于最底端繼續(xù)上拉時(shí)讓footView的height隨滑動而變化。
ListView本身支持添加headView和footView窑多,但RecyclerView不支持仍稀,所以需要重寫RecyclerView.Adapter
RecyclerView是否開啟嵌套滑動決定了我們實(shí)現(xiàn)RecyclerView下拉刷新和上拉加載功能的難易程度,開啟嵌套滑動時(shí)我們可以直接利用SwipeRefreshLayout支持的嵌套滑動方法來實(shí)現(xiàn)埂息。其他情況都需要攔截事件進(jìn)行滑動處理技潘,我們看一下下面這張圖對于事件進(jìn)行攔截處理的分類情況
??從圖中我們可以具體看到什么情況下需要攔截事件,我們這個(gè)自定義SwipeRefreshLayout實(shí)現(xiàn)的關(guān)鍵地方就是利用嵌套滑動或者事件攔截來處理千康,下面進(jìn)入正題看下到底該怎么做享幽。
- 類的說明
類 | 描述 |
---|---|
SwipeRefreshLoadLayout | 繼承SwipeRefreshLayout,增加上拉加載功能以及各種事件的處理拾弃,整個(gè)項(xiàng)目的和心類 |
RecycleViewAdapter | 如果要用RecyclerView實(shí)現(xiàn)功能值桩,使用此類代替原RecyclerView.Adapter |
SwipeLinearLayoutManager | RecyclerView布局管理器,繼承LinearLayoutManager豪椿,一個(gè)輔助類奔坟,使用RecyclerView時(shí)必須使用此類 |
Swipe | 內(nèi)部有公開和非公開的監(jiān)聽接口 |
ListViewHeadAndFootManager | ListView的headView和footView管理器 |
3.通過嵌套滑動方式實(shí)現(xiàn)
??我們從最簡單的地方入手,就是當(dāng)RecyclerView開啟嵌套滑動功能的情況下實(shí)現(xiàn)延展樣式搭盾,這個(gè)時(shí)候SwipeRefreshLayout能監(jiān)聽到RecyclerVIew是否滑動到了最頂端或最底部咳秉,我們需要覆寫它下面幾個(gè)方法:
- void onNestedScrollAccepted(View child, View target, int axes):開啟嵌套滑動功能后才會調(diào)用,僅在滑動開始時(shí)調(diào)用一次鸯隅,可進(jìn)行滑動相關(guān)數(shù)據(jù)的初始化操作澜建。
- void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed):當(dāng)RecyclerView滑動到頂端并繼續(xù)下拉或者滑動到底部并繼續(xù)上拉時(shí)SwipeRefreshLayout會調(diào)用此方法,覆寫此方法后可通過dyUnconsumed參數(shù)獲取到當(dāng)前滑動的距離滋迈,從而計(jì)算出headView或footView的height增大的值霎奢。
- void onNestedPreScroll(View target, int dx, int dy, int[] consumed):當(dāng)從下拉刷新或上拉加載狀態(tài)返回時(shí),SwipeRefreshLayout會調(diào)用此方法饼灿,覆寫此方法可計(jì)算headView或footView的height減小的值幕侠。
- void onStopNestedScroll(View target):手指離開屏幕時(shí)SwipeRefreshLayout會調(diào)用此方法,覆寫此方法我們可以從這個(gè)狀態(tài)開始計(jì)算headView或footView回收時(shí)其height的值碍彭。
??同理晤硕,按照這個(gè)思路,上拉加載也完全可以通過覆寫這四個(gè)方法實(shí)現(xiàn)庇忌,當(dāng)然舞箍,這是在RecyclerView開啟嵌套滑動的前提下,我們結(jié)合代碼看一下這部分的具體實(shí)現(xiàn)皆疹。
覆寫SwipeRefreshLayout類中四個(gè)關(guān)于嵌套滑動的方法
//下拉刷新時(shí)手指的滑動距離疏橄,意味著headView的height需要改變的大小
private int mTotalUnconsumed;
//上拉加載時(shí)手指的滑動距離,意味著footView的height需要改變的大小
private int mTotalUnconsumed2;
@Override
public void onNestedScrollAccepted(View child, View target, int axes) {
super.onNestedScrollAccepted(child, target, axes);
mTotalUnconsumed = 0;
mTotalUnconsumed2 = 0;
}
/**
* 下拉刷新返回或者上拉加載返回時(shí)需要覆寫的方法,
* 通過dy計(jì)算headView或者footView的height值捎迫,減小其高度晃酒,
* 通過consumed[]改變RecyclerView的滑動距離,使之不會滑動的過快造成滑動效果降低窄绒,
* 這里我們通過下拉刷新樣式?jīng)Q定是調(diào)用父類的方法還是自己的方法贝次,如果時(shí)需要原生樣式,直接調(diào)用父類方法彰导,
* 否則調(diào)用自己的方法
*/
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
if (refreshStyle == CIRCLE) {
super.onNestedPreScroll(target, dx, dy, consumed);
} else if (refreshStyle == SPREAD) {
if (dy > 0 && mTotalUnconsumed > 0) {
//下拉刷新返回操作
if (dy > mTotalUnconsumed) {
consumed[1] = dy - mTotalUnconsumed;
mTotalUnconsumed = 0;
finishParentDrag();
} else {
mTotalUnconsumed -= dy;
consumed[1] = dy;
}
onPullDownBack(dy);
}
}
//上拉加載返回操作
if (dy < 0 && mTotalUnconsumed2 < 0) {
if (dy < mTotalUnconsumed2) {
consumed[1] = mTotalUnconsumed2 - dy;
mTotalUnconsumed2 = 0;
finishParentDrag();
} else {
mTotalUnconsumed2 -= dy;
consumed[1] = dy;
}
onPullUpBack(dy);
}
}
/**
* 下拉刷新或者上拉加載時(shí)需要覆寫的方法蛔翅,
* 通過dyUnconsumed計(jì)算headView或者footView的height值,增大其高度位谋,
* 通過下拉刷新樣式?jīng)Q定是調(diào)用父類的方法還是自己的方法山析,如果時(shí)需要原生樣式,直接調(diào)用父類方法倔幼,
* 否則調(diào)用自己的方法
*/
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
if (refreshStyle == CIRCLE) {
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
} else if (refreshStyle == SPREAD) {
//下拉刷新操作
if (dyUnconsumed < 0 && !isRefreshing) {
mTotalUnconsumed += -dyUnconsumed;
onPullDown(dyUnconsumed);
}
}
//上拉加載操作
if (dyUnconsumed > 0 && !isLoading && footViewVisibility == VISIBLE) {
mTotalUnconsumed2 += -dyUnconsumed;
onPullUp(dyUnconsumed);
}
}
/**
* 手指釋放時(shí)調(diào)用盖腿,開啟一個(gè)動畫讓headView或者footView的height恢復(fù)
*/
@Override
public void onStopNestedScroll(View target) {
if (refreshStyle == CIRCLE) {
super.onStopNestedScroll(target);
} else if (refreshStyle == SPREAD) {
//下拉刷新釋放操作
if (mTotalUnconsumed > 0 && !isRefreshing) {
release();
mTotalUnconsumed = 0;
}
}
//上拉加載釋放操作
if (mTotalUnconsumed2 < 0) {
release();
mTotalUnconsumed2 = 0;
}
}
??對于以上代碼來說,我們加入了下拉刷新的樣式判斷损同,如果樣式是CIRCLE即SwipeRefreshLayout原生樣式翩腐,那就直接調(diào)用父方法,否則如果是SPREAD即延展樣式膏燃,我們才需要調(diào)用我們自己的實(shí)現(xiàn)代碼茂卦。
??下面是下拉刷新或上拉加載時(shí)執(zhí)行的代碼,會分別調(diào)用onPullDown(int dy)组哩、onPullDownBack(int dy)等龙、onPullUp(int dy)、onPullUpBack(int dy)方法伶贰,然后通過Swipe.OnChangeViewHeight接口讓headView或footView的height發(fā)生變化改變它的高度蛛砰,這里只貼一小段代碼,具體的可查看項(xiàng)目源碼黍衙。
SwipeRefreshLoadLayout中:
/**
* 下拉刷新
*/
private void onPullDown(int dy) {
dragAction = DRAG_ACTION_PULL_DOWN;
pullDownDistance += Math.abs(dy) / 2;
changeTipsRefresh();
if (onChangeViewHeight != null) {
onChangeViewHeight.changeHeadViewHeight(pullDownDistance);
}
}
RecycleViewAdapter實(shí)現(xiàn)了Swipe.OnChangeViewHeight接口泥畅,其中代碼:
@Override
public void changeHeadViewHeight(int headViewHeight) {
if (headViewHolder.headView != null && headViewHeight >= 0) {
headViewLayoutParams.height = headViewHeight;
headViewHolder.headView.setLayoutParams(headViewLayoutParams);
}
}
??現(xiàn)在,通過SwipeRefreshLayout的幫助我們RecyclerView的下拉刷新和上拉加載已經(jīng)實(shí)現(xiàn)了琅翻,但當(dāng)RecyclerView禁止嵌套滑動時(shí)位仁,上面的四個(gè)方法就會無法使用,并且如果我們要實(shí)現(xiàn)ListView的這一功能方椎,我們必須要通過事件攔截來實(shí)現(xiàn)聂抢。
4.通過事件攔截方式實(shí)現(xiàn)
??事件分發(fā)我們不多做介紹了,這里就兩點(diǎn)要說的棠众,什么時(shí)候攔截事件琳疏,攔截以后要做什么,這是我們這篇自定義ViewGroup要面臨的問題。在RecyclerView中空盼,我們通過View類的boolean canScrollVertically(int direction)方法確定它是否滑到了最頂端或最底部疮薇。在ListView中,我們通過ListViewCompat類的boolean canScrollList(ListView listView, int direction)判斷它是否滑到了最頂端或最底部我注,有了這兩個(gè)方法,我們在onInterceptTouchEvent(MotionEvent ev)
中進(jìn)行判斷即可迟隅,符合情況的我們返回true然后交給onTouchEvent(MotionEvent ev)處理但骨,不符合的直接調(diào)用父方法即可。
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (action == null) {
return super.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
float currentY = ev.getY();
int dy = (int) (currentY - initialDownY);
if (dy >= 0) {//dy>0說明此時(shí)執(zhí)行的是下拉刷新或者上拉加載返回操作
if (action == ACTION_PULL_DOWN && !isRefreshing) {
onPullDown(dy);
} else if (action == ACTION_PULL_UP && !isLoading) {
onPullUpBack(dy);
if (recyclerView != null) {
recyclerView.scrollBy(0, dy);
}
}
} else {//dy<0說明此時(shí)執(zhí)行的是上拉加載或者下拉刷新返回操作
if (action == ACTION_PULL_DOWN && !isRefreshing) {
onPullDownBack(dy);
} else if (action == ACTION_PULL_UP && !isLoading && footViewVisibility == VISIBLE) {
onPullUp(dy);
if (recyclerView != null) {
recyclerView.scrollBy(0, -dy);
}
if (listView != null) {
ListViewCompat.scrollListBy(listView, -dy);
}
}
}
initialDownY = currentY;
break;
case MotionEvent.ACTION_UP:
action = null;
release();
break;
}
return action != null || super.onTouchEvent(ev);
}
private float initialDownY;
private final int ACTION_PULL_DOWN = 0X00C1;
private final int ACTION_PULL_UP = 0X00D1;
private Integer action = null;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (recyclerView != null) {
//當(dāng)刷新類型為CIRCLE且處于刷新狀態(tài)時(shí)智袭,recyclerView的嵌套滑動不再響應(yīng)奔缠,所以無法進(jìn)行上拉加載,因此通過攔截事件利用onTouchEvent()實(shí)現(xiàn)上拉加載
if (refreshStyle == CIRCLE && isRefreshing && !recyclerView.canScrollVertically(1)) {
Boolean x = isIntercept(ev, true);
if (x != null) return x;
}
//當(dāng)recyclerView禁止嵌套滑動時(shí)吼野,上拉加載需要使用onTouchEvent()實(shí)現(xiàn)校哎,所以這里要進(jìn)行攔截
if (!recyclerView.isNestedScrollingEnabled() && !recyclerView.canScrollVertically(1)) {
Boolean x = isIntercept(ev, true);
if (x != null) return x;
}
//當(dāng)刷新類型為SPREAD時(shí),如果recyclerView禁止了嵌套滑動瞳步,那這里需要攔截事件讓onTouchEvent()實(shí)現(xiàn)下拉刷新
if (refreshStyle == SPREAD && !recyclerView.isNestedScrollingEnabled() && !recyclerView.canScrollVertically(-1)) {
Boolean x = isIntercept(ev, false);
if (x != null) return x;
}
}
if (listView != null) {
if (refreshStyle == CIRCLE && isRefreshing && !ListViewCompat.canScrollList(listView, 1)) {
Boolean x = isIntercept(ev, true);
if (x != null) return x;
}
if (!ListViewCompat.canScrollList(listView, 1)) {
Boolean x = isIntercept(ev, true);
if (x != null) return x;
}
if (refreshStyle == SPREAD && !ListViewCompat.canScrollList(listView, -1)) {
Boolean x = isIntercept(ev, false);
if (x != null) return x;
}
}
return super.onInterceptTouchEvent(ev);
}
@Nullable
private Boolean isIntercept(MotionEvent ev, boolean type) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
initialDownY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float y = ev.getY();
if (type) {
if (y - initialDownY < 0) {//判斷上滑或者下滑操作
initialDownY = y;
action = ACTION_PULL_UP;
return true;
}
} else {
if (y - initialDownY > 0) {
initialDownY = y;
action = ACTION_PULL_DOWN;
return true;
}
}
break;
}
return null;
}
??上面這段代碼實(shí)現(xiàn)了何時(shí)攔截事件的邏輯闷哆,以及攔截后的相應(yīng)處理。當(dāng)然单起,這只是我個(gè)人實(shí)現(xiàn)的一種方式或許并不適用于所有人抱怔,對于每個(gè)人而言都各有一種實(shí)現(xiàn)方式,所以我也不準(zhǔn)備強(qiáng)行解釋上面的代碼了嘀倒,以上這段代碼只作為參考屈留,提供一種實(shí)現(xiàn)思路。
??我們可以看到测蘑,在事件攔截后灌危,將相應(yīng)的操作交給了onTouchEvent()方法處理,在onTouchEvent()方法中碳胳,通過判斷上下滑動勇蝙,確定手指滑動的距離,調(diào)用onPullDown(int dy)固逗、onPullDownBack(int dy)浅蚪、onPullUp(int dy)、onPullUpBack(int dy)方法來改變headView和footView的高度烫罩,從而達(dá)到之前利用嵌套滑動方式同樣的效果惜傲。
??現(xiàn)在,我們實(shí)現(xiàn)了圖中所有情況的下拉刷新和上拉加載操作贝攒,我們再看看其他幾個(gè)類盗誊。
5.RecycleViewAdapter類
??我們知道在RecyclerView中需要手動添加headView和footView,所以我們需要繼承RecyclerView.Adapter實(shí)現(xiàn)一個(gè)自己的Adapter,在這個(gè)Adapter中哈踱,需要實(shí)現(xiàn)原有的Adapter抽象方法荒适,并再次抽象出相應(yīng)的方法供外部使用,看一下該類中和原有Adapter的對應(yīng)方法开镣, 我們使用時(shí)只需要實(shí)現(xiàn)這幾個(gè)新的抽象方法即可刀诬。
RecyclerView.Adapter的原有方法 | RecycleViewAdapter中對應(yīng)的方法 |
---|---|
int getItemCount() | int getCounts() |
RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) | RecyclerView.ViewHolder onNewViewHolder(@NonNull ViewGroup parent, int viewType) |
void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) | void onSetViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) |
int getItemViewType(int position) | int getItemType(int position) |
- 有一點(diǎn)要注意,在使用RecyclerView.Adapter類中其他沒有覆寫過的方法(也就是除了上述表中的其他方法)時(shí)要注意position邪财,因?yàn)槲覀兊腞ecyclerView默認(rèn)已經(jīng)有兩個(gè)View了陕壹,headView和footView,所以在碰到有方法中有position時(shí)要注意當(dāng)前position具體是哪一個(gè)树埠,一般真正的值是position-1糠馆,因?yàn)橐獪p去一個(gè)headView。
自定義headView或footView
??此外怎憋,在此類中額外實(shí)現(xiàn)了一個(gè)功能又碌,那就是可以自定義ViewGrou實(shí)現(xiàn)延展樣式的headView和footView,如果不滿意我實(shí)現(xiàn)的樣式绊袋,可以自己定義毕匀。實(shí)現(xiàn)此功能的代碼如下:
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_FOOT) {
if (footViewHolder == null) {
//在這里判斷一下是否有自定義的ViewGroup作為headView或footView
if (mySwipe.getFootView() == null) {
footViewHolder = new FootViewHolder(LayoutInflater.from(mContext).inflate(R.layout.srll_foot, null, false), true);
} else {
footViewHolder = new FootViewHolder(mySwipe.getFootView(), false);
}
}
return footViewHolder;
} else if (viewType == VIEW_TYPE_HEAD) {
if (headViewHolder == null) {
if (mySwipe.getHeadView() == null) {
headViewHolder = new HeadViewHolder(LayoutInflater.from(mContext).inflate(R.layout.srll_head, null, true), true);
} else {
headViewHolder = new HeadViewHolder(mySwipe.getHeadView(), false);
}
}
return headViewHolder;
}
return onNewViewHolder(parent, viewType);
}
private class FootViewHolder extends RecyclerView.ViewHolder {
private TextView tvFootTip;
private ViewGroup footView;
private ImageView ivFootRefresh;
private FootViewHolder(View itemView, boolean isFromSelf) {
super(itemView);
this.footView = (ViewGroup) itemView;
this.footView.setVisibility(mySwipe.footViewVisibility);
if (isFromSelf) {
ViewGroup childAt = (ViewGroup) footView.getChildAt(0);
ViewGroup.LayoutParams childLayoutParams = childAt.getLayoutParams();
childLayoutParams.height = refreshViewHeight;
//設(shè)置底部加載子視圖高度
childAt.setLayoutParams(childLayoutParams);
this.tvFootTip = footView.findViewById(R.id.tv_foot_tip);
this.ivFootRefresh = footView.findViewById(R.id.iv_foot_refresh);
this.ivFootRefresh.animate().setInterpolator(new LinearInterpolator());
LinearLayout llLoadMore = footView.findViewById(R.id.ll_load_more);
llLoadMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mySwipe.doLoadMore();
}
});
}
//設(shè)置底部加載父視圖高度
footViewLayoutParams = new RecyclerView.LayoutParams(-1, refreshViewHeight);
footView.setLayoutParams(footViewLayoutParams);
}
}
private class HeadViewHolder extends RecyclerView.ViewHolder {
private ViewGroup headView;
private TextView tvHeadTip;
private ImageView ivHeadArrow;
private TextView tvRefreshTime;
private ImageView ivHeadRefresh;
private HeadViewHolder(View itemView, boolean isFromSelf) {
super(itemView);
this.headView = (ViewGroup) itemView;
if (isFromSelf) {
ViewGroup childAt = (ViewGroup) headView.getChildAt(0);
ViewGroup.LayoutParams childLayoutParams = childAt.getLayoutParams();
childLayoutParams.height = refreshViewHeight;
childAt.setLayoutParams(childLayoutParams);
this.tvHeadTip = headView.findViewById(R.id.tv_head_tip);
this.ivHeadArrow = headView.findViewById(R.id.iv_head_arrow);
this.ivHeadArrow.animate().setInterpolator(new LinearInterpolator());
this.ivHeadRefresh = headView.findViewById(R.id.iv_head_refresh);
this.ivHeadRefresh.animate().setInterpolator(new LinearInterpolator());
this.tvRefreshTime = headView.findViewById(R.id.tv_refresh_time);
if (!TextUtils.isEmpty(Swipe.getLastRefreshTime(mContext))) {
this.tvRefreshTime.setText("最后更新:" + Swipe.getLastRefreshTime(mContext));
} else {
this.tvRefreshTime.setVisibility(View.GONE);
}
}
headViewLayoutParams = new RecyclerView.LayoutParams(-1, 0);
headView.setLayoutParams(headViewLayoutParams);
}
}
??原理很簡單,在onCreateViewHolder()中癌别,先判斷是否有自定義的ViewGroup作為headView或者footView期揪,如果有,傳入ViewHolder中规个,作為當(dāng)前的headView或footView凤薛,當(dāng)我們做下拉刷新或者上拉加載時(shí),就動態(tài)的改變此ViewGroup的height诞仓,當(dāng)然缤苫,此時(shí)只能做到把當(dāng)前headView或footView拉伸或壓縮,無法改變其他的狀態(tài)(比如自己定義的箭頭圖片或者刷新圖片什么時(shí)候做出相應(yīng)的變化)墅拭,所以需要一個(gè)接口來監(jiān)聽我們做下拉刷新或上拉加載的狀態(tài)活玲,繼續(xù)往下看。
6.Swipe類
??這是一個(gè)為了滿足各種監(jiān)聽和通知實(shí)現(xiàn)的一個(gè)類谍婉,里面是各種接口舒憾,其中有兩個(gè)public接口和兩個(gè)protect接口。
/**
* 上拉和下拉時(shí)滑動監(jiān)聽
*/
public interface OnSlideActionListener {
/**
* 釋放刷新行為
*/
void releaseRefreshAction();
/**
* 下拉刷新行為
*/
void downRefreshAction();
/**
* 釋放加載行為
*/
void releaseLoadAction();
/**
* 上拉加載行為
*/
void upLoadAction();
}
/**
* 改變頭尾提示信息穗熬,僅限本包類
*/
interface OnChangeViewTip {
/**
* 改變底部view提示
*
* @param tips
*/
void changeFootTips(String tips);
/**
* 改變頭view提示
*
* @param tips
*/
void changeHeadTips(String tips);
}
/**
* 監(jiān)聽刷新和加載更多
*/
public interface OnRefreshAndLoadListener {
/**
* 下拉刷新
*/
void refresh();
/**
* 上拉加載
*/
void loadMore();
}
/**
* 監(jiān)聽頭尾view的高度變化
*/
interface OnChangeViewHeight {
/**
* 改變頭view高度
*
* @param headViewHeight
*/
void changeHeadViewHeight(int headViewHeight);
/**
* 改變底部view高度
*
* @param footViewHeight
*/
void changeFootViewHeight(int footViewHeight);
}
- OnSlideActionListener:上拉和下拉時(shí)滑動監(jiān)聽镀迂,一般不用實(shí)現(xiàn),它監(jiān)聽了所有的滑動事件唤蔗,只有在自定義headView和footView時(shí)需要實(shí)現(xiàn)這個(gè)接口探遵,可以通過這個(gè)接口中的方法改變狀態(tài)窟赏。
- OnChangeViewTip:用protect修飾,不對外開放箱季,改變提示信息涯穷,如是自定義headView或footView,直接實(shí)現(xiàn)OnSlideActionListener接口即可藏雏,所以開放此接口無意義拷况,僅由RecycleViewAdapter類和ListViewHeadAndFootManager類實(shí)現(xiàn)。
- OnRefreshAndLoadListener:刷新或加載監(jiān)聽掘殴。
- OnChangeViewHeight:改變headView或footView高度蝠嘉,實(shí)現(xiàn)延展樣式,用protect修飾杯巨,由RecycleViewAdapter類和ListViewHeadAndFootManager類實(shí)現(xiàn)。
??以上這些接口在SwipeRefreshLoadLayout類中都有調(diào)用的地方努酸,都非常容易理解所以不貼代碼了服爷,感興趣的可以看下整個(gè)項(xiàng)目。
7.ListViewHeadAndFootManager類
??此類和RecycleViewAdapter類功能相似获诈,由于ListView本身有setHeadView()和setFootView()方法仍源,所以不需要繼承BaseAdapter來實(shí)現(xiàn)headView和footView,里面的代碼和RecycleViewAdapter的代碼大致相同舔涎,所以就不多做介紹了笼踩。
8.SwipeLinearLayoutManager類
??繼承于LinearLayoutManager類,里面只覆寫了一個(gè)方法:
@Override
public int getDecoratedBottom(View child) {
if (mySwipe != null && child.getId() == mySwipe.headViewId) {
return 1 + getBottomDecorationHeight(child);
}
return super.getDecoratedBottom(child);
}
- 我們在使用RecyclerView時(shí)亡嫌,禁用嵌套滑動的情況下需要使用boolean canScrollVertically(int direction)方法判斷是否滑到了最頂端或最底部嚎于,當(dāng)滑到最頂端時(shí),如果headView的height為0挟冠,那么此方法就會判斷失誤于购,無法觸發(fā)下拉刷新,具體原因可以追蹤一下源碼知染,此方法會調(diào)用一次int getDecoratedBottom(View child)方法肋僧,我們只要判斷當(dāng)child為headView時(shí)返回值大于0即可。
??以上控淡,就是本問自定義ViewGroup的全部內(nèi)容了嫌吠,具體使用方法和源碼可以在GitHub上查看: