前面第一章和第二章講述了如何實(shí)現(xiàn)一個(gè)基本的上拉下拉控件,接下來(lái)討論一下里面關(guān)鍵的幾個(gè)類的實(shí)現(xiàn)以及如何增強(qiáng)功能
- 工具類實(shí)現(xiàn)
- 沒(méi)有數(shù)據(jù)時(shí)的空白頁(yè)面支持
ScrollViewCompat工具類
我們?cè)谏侠吕瓌?dòng)作的時(shí)候,需要通過(guò)ScrollViewCompat
工具類來(lái)判斷是否達(dá)到頂端或者底端壤玫,一般可滑動(dòng)的控件分為ScrollView
,ListView
,WebView
和RecyclerView
询件,ListView
和RecyclerView
是通過(guò)當(dāng)前顯示的子視圖是否第一個(gè)或者最后一個(gè)以及是否有未顯示的視圖判斷缚忧,ScrollView
是通過(guò)scrollY值判斷
/**
* 該視圖控件還能否向下拉動(dòng)
*
* @param mTarget
* @return true-未到頂部僵控,false-到頂部
*/
public static boolean canSmoothDown(View mTarget) {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else if (mTarget instanceof RecyclerView) {
final RecyclerView recyclerView = (RecyclerView) mTarget;
LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
if ((lm.findFirstVisibleItemPosition() == 0)) {
View firstView = lm.findViewByPosition(0);
return firstView.getTop() < 0;
} else {
return true;
}
} else {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
/**
* 該視圖控件還能否向上拉動(dòng)
*
* @param mTarget
* @return true-未到底部,false-到底部
*/
public static boolean canSmoothUp(View mTarget) {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
if (lastChild != null) {
if (absListView.getFirstVisiblePosition() == 0 && absListView.getLastVisiblePosition() == (absListView.getCount() - 1)) {
return false;
}
return (absListView.getLastVisiblePosition() < (absListView.getCount() - 1))
&& lastChild.getBottom() > absListView.getPaddingBottom();
} else {
return false;
}
} else if (mTarget instanceof RecyclerView) {
final RecyclerView recyclerView = (RecyclerView) mTarget;
LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
int count = recyclerView.getAdapter().getItemCount() - 1;
if (lm.canScrollVertically()) {
return !(lm.findLastVisibleItemPosition() == count);
} else {
return false;
}
} else {
View scrollChild = ((ViewGroup) mTarget).getChildAt(0);
if (scrollChild == null) {
return false;
} else {
int childHeight = scrollChild.getMeasuredHeight();
return (mTarget.getScrollY() + mTarget.getHeight()) < childHeight;
}
}
} else {
return ViewCompat.canScrollVertically(mTarget, 1);
}
}
空白頁(yè)面支持
一般在沒(méi)有數(shù)據(jù)的時(shí)候我們希望顯示一個(gè)缺省的空白頁(yè)面图柏,類似于ListView
的emptyView
這里除了添加上空白頁(yè)面支持外序六,還需做一些小的改變。在顯示空白頁(yè)面時(shí)蚤吹,我們希望只有動(dòng)畫可拽動(dòng)而空白頁(yè)面不會(huì)被拽動(dòng)例诀,也就是侵入式的下拉效果(非侵入式效果即之前的正常列表的下拉)
public DragRefreshLayout(Context context, AttributeSet attrs) {
// .......
emptyId = a.getResourceId(R.styleable.refresh_DragRefreshLayout_refresh_empty, 0);
// .......
}
private void ensureTarget() {
// .....
if (emptyId != 0) {
emptyView = findViewById(emptyId);
emptyView.setClickable(true);
}
}
VDH的tryCaptureView
中增加是否是emptyView
的判斷
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mTarget
|| (child == emptyView && emptyView.isShown())
|| child == refreshView
|| child == loadView;
}
不要忘了clampViewPositionVertical
:
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (child == mTarget || (child == emptyView && emptyView.isShown())) {
status = ScrollStatus.DRAGGING;
if (contentTop + dy > DRAG_MAX_RANGE) {
return DRAG_MAX_RANGE;
} else if (contentTop + dy < -DRAG_MAX_RANGE) {
return -DRAG_MAX_RANGE;
} else {
return top;
}
} else {
status = ScrollStatus.DRAGGING;
if (contentTop + dy > DRAG_MAX_RANGE) {
return DRAG_MAX_RANGE - refreshView.getMeasuredHeight();
} else if (contentTop + dy < -DRAG_MAX_RANGE) {
return getMeasuredHeight() - getPaddingBottom() - DRAG_MAX_RANGE;
} else {
return top;
}
}
}
侵入式下拉即下拉時(shí)emptyView
的位置不變,其他視圖的位置依然變化:
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if (changedView == mTarget) {
//......
} else if (changedView == emptyView && emptyView.isShown()) {
refreshView.offsetTopAndBottom(dy);
loadView.offsetTopAndBottom(dy);
contentTop = top;
invalidate();
} else {
// .......
}
}
因?yàn)榭丶痪S護(hù)數(shù)據(jù)內(nèi)容裁着,控件本身沒(méi)有設(shè)置空白頁(yè)面展示與隱藏的能力繁涂,不比
ListView
,因此只能在業(yè)務(wù)中判斷數(shù)據(jù)是否為空并且顯示或者隱藏空白頁(yè)面
到此二驰,整個(gè)DragRefresh
控件完成扔罪,have a happy day!