Android控件PullRefreshViewGroup實(shí)現(xiàn)下拉刷新和上拉加載
本文實(shí)例為大家分享了Android實(shí)現(xiàn)下拉刷新和上拉加載更多的具體代碼,供大家參考,具體內(nèi)容如下
實(shí)現(xiàn)思路:由PullRefreshViewGroup控件來(lái)接管標(biāo)準(zhǔn)控件(比如RecyclerView、ListView等)的滑動(dòng),調(diào)用標(biāo)準(zhǔn)控件的內(nèi)部方法進(jìn)行短距離滑動(dòng),不再由標(biāo)準(zhǔn)控件自己來(lái)處理事件,而完全由PullRefreshViewGroup控件來(lái)處理觸摸事件。標(biāo)準(zhǔn)控件內(nèi)部的滑動(dòng)距離等屬性,通過(guò)反射獲得computeVerticalScrollExtent、computeVerticalScrollRange、computeVerticalScrollOffset這三個(gè)方法來(lái)獲得。
PullRefreshViewGroup控件的布局如下

部分代碼實(shí)現(xiàn)
觸摸滑動(dòng)事件處理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean bret = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mPreY = ev.getY();
Log.d(TAG, "mPreY:" + String.valueOf(mPreY));
}
break;
case MotionEvent.ACTION_MOVE: {
float curY = ev.getY();
float distance = curY - mPreY;
if (Math.abs(distance) >= mTouchSlop) {
mSliding = bret = true;
//修正第一次滑動(dòng)的卡頓
if (distance > 0) {
mPreY += mTouchSlop;
} else {
mPreY -= mTouchSlop;
}
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
} else {
mSliding = bret = false;
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
bret = mSliding;
}
break;
}
return bret ? true : super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean bret = false;
vTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mPreY = event.getY();
bret = true;
}
break;
case MotionEvent.ACTION_MOVE: {
float curY = event.getY();
float distance = curY - mPreY;
Log.d(TAG, "mPreY:" + String.valueOf(mPreY) + " distance:" + String.valueOf(distance));
if (distance != 0) {
bret = true;
if (mSrcHeightHead == -1 && mHasPullRefresh) {
View child0View = mHeadView;
mSrcHeightHead = child0View.getHeight();
}
scrollBy(distance);
}
mPreY = curY;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mPreCurY = 0;
View child0View = mHeadView;
int child0Height = null != child0View ? child0View.getHeight() : 0;
int child0Height2 = null != child0View ? child0View.getLayoutParams().height : 0; //視圖的最終高度是有這個(gè)來(lái)決定的,請(qǐng)看onMeasure 函數(shù)的實(shí)現(xiàn)
// int child0Height3 = child0View.getMeasuredHeight();
if (child0Height2 != child0Height) {
child0Height = child0Height2;
}
int child0Top = null != child0View ? child0View.getTop() : 0;
int dy = child0Height - mSrcHeightHead + (mSrcHeightHead - Math.abs(child0Top));
Log.d(TAG, "onTouchEvent()ACTION_UP child0Height:" + String.valueOf(child0Height) + " mSrcHeightHead:" + String.valueOf(mSrcHeightHead) + " child0Top:" + String.valueOf(child0Top));
if (dy > 0) {//恢復(fù)拉伸視圖的位置
if (!mLoadingMore && dy > mCanRefreshHeight && child0Top + child0Height2 > mCanRefreshHeight && mRefreshLoad != null) {
dy -= mCanRefreshHeight;
if (!mPullRefreshLoading) {
mPullRefreshLoading = true;
mTvRefreshInfo.setText("正在加載...");
mRefreshLoad.pullRefreshStartLoad();
}
}
mScroller.startScroll(0, 0, 0, -dy);
invalidate();
} else {
vTracker.computeCurrentVelocity(1000);
float yvel = vTracker.getYVelocity();
if (yvel != 0) {//為了滿(mǎn)足內(nèi)部視圖的快速滾動(dòng)( 中間內(nèi)容視圖 )
mScroller.fling(0, 0, 0, (int) yvel, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
invalidate();
}
}
vTracker.clear();
bret = true;
}
break;
}
return bret ? true : super.onTouchEvent(event);
}
小距離滑動(dòng)代碼
private void scrollBy(float distance) {
View child0View = mHeadView;
View child1View = getChildAt(null == mHeadView ? 0 : 1);
float distanceRemain = 0;
int child0Top = null != child0View ? child0View.getTop() : 0;
// int child0Height = child0View.getHeight();
if (distance < 0) {//向上
int child1Top = child1View.getTop();
// int child1Height = child1View.getHeight();
//child0View 縮小
if (-1 != mSrcHeightHead && null != child0View && child0View.getHeight() > mSrcHeightHead) {
float off = distance;
if (child0View.getHeight() + distance < mSrcHeightHead) {
off = -(child0View.getHeight() - mSrcHeightHead);
distance -= off;
} else {
distance = 0;
}
child0View.getLayoutParams().height += (int) off;
child1Top += (int) off; //child0view 縮小的同時(shí), child1view 的高度也會(huì)隨之上升 這里很重要
requestLayout();
child1View.offsetTopAndBottom((int) off);
if (null != mTailView) {
mTailView.offsetTopAndBottom((int) off);
}
}
if (distance != 0) {
if (child0Top + mSrcHeightHead + distance <= 0) {
distanceRemain = -(distance + (child0Top + mSrcHeightHead));//正數(shù)
distance = -(child0Top + mSrcHeightHead);//負(fù)數(shù)
}
//可以顯示加載更多嗎?
boolean bDirectDown = false;
boolean bCanScroll = child1View.canScrollVertically(1) || child1View.canScrollVertically(-1);
if (!bCanScroll) {
int child1ChildCount = 0;
if (child1View instanceof ViewGroup) {
child1ChildCount = ((ViewGroup) child1View).getChildCount();
}
if (child1ChildCount > 0) {
ViewGroup viewGroupChild1 = (ViewGroup) child1View;
View child1LastItem = viewGroupChild1.getChildAt(child1ChildCount - 1);
int child1ViewBottom = viewGroupChild1.getBottom();
int child1LastItemBottom = child1LastItem.getBottom() + child1Top; //相對(duì)于 ImageScaleViewGroup 的位置
//增加 child1ViewBottom > getHeight() 來(lái)控制 ScrollView
if (child1LastItemBottom == getHeight()) {
bDirectDown = true;
}
}
}
//正在下拉刷新的時(shí)候,不能顯示加載更多
if ((bCanScroll || bDirectDown) && null != mTailView && !mPullRefreshLoading) {
int nVerticalScrollExtent = 0, nVerticalScrollRange = 0, nVerticalScrollOffset = 0;
Class c = null;
try {
c = Class.forName(child1View.getClass().getName());
} catch (Exception ex) {
}
try {
if (null == mComputeVerticalScrollExtent) {
Method computeVerticalScrollExtent = findcomputeVerticalMethod(c, "computeVerticalScrollExtent");
computeVerticalScrollExtent.setAccessible(true);
mComputeVerticalScrollExtent = computeVerticalScrollExtent;
}
nVerticalScrollExtent = (int) mComputeVerticalScrollExtent.invoke(child1View);
} catch (Exception ex) {
}
try {
if (null == mComputeVerticalScrollRange) {
Method computeVerticalScrollRange = findcomputeVerticalMethod(c, "computeVerticalScrollRange");
computeVerticalScrollRange.setAccessible(true);
mComputeVerticalScrollRange = computeVerticalScrollRange;
}
nVerticalScrollRange = (int) mComputeVerticalScrollRange.invoke(child1View);
} catch (Exception ex) {
}
try {
if (null == mComputeVerticalScrollOffset) {
Method computeVerticalScrollOffset = findcomputeVerticalMethod(c, "computeVerticalScrollOffset");
computeVerticalScrollOffset.setAccessible(true);
mComputeVerticalScrollOffset = computeVerticalScrollOffset;
}
nVerticalScrollOffset = (int) mComputeVerticalScrollOffset.invoke(child1View);
} catch (Exception ex) {
}
int range = nVerticalScrollRange - nVerticalScrollExtent;
if (nVerticalScrollOffset + distanceRemain > range) {
float nOff = distanceRemain - (range - nVerticalScrollOffset);
distanceRemain = range - nVerticalScrollOffset;
distance -= nOff;
}
int child3Bottom = mTailView.getBottom();
if (child3Bottom + distance < getHeight()) {
distance = getHeight() - child3Bottom;
}
}
if (!bCanScroll) {
distanceRemain = 0;
}
}
} else {//向下
int nScrollOffset = 0;
try {
Class c = Class.forName(child1View.getClass().getName());
Method computeVerticalScrollOffset = findcomputeVerticalMethod(c, "computeVerticalScrollOffset");//c.getDeclaredMethod("computeVerticalScrollOffset");
computeVerticalScrollOffset.setAccessible(true);
nScrollOffset = (int) computeVerticalScrollOffset.invoke(child1View);
} catch (Exception ex) {
}
int child2Top = null != mTailView ? mTailView.getTop() : getHeight();//注意默認(rèn)值
if (child2Top < getHeight()) {
if (child2Top + distance > getHeight()) {
distanceRemain = distance - (getHeight() - child2Top);
distance = getHeight() - child2Top;
}
} else if (nScrollOffset > 0) {//內(nèi)部有滾動(dòng),那么就要計(jì)算內(nèi)部滾動(dòng)距離,其他分配給整體滾動(dòng)
if (nScrollOffset - distance <= 0) {
distanceRemain = -nScrollOffset;
// distance = distance - nScrollOffset;
distance = 0; //內(nèi)部能滾動(dòng),不讓外部去滾動(dòng)
if (!mScroller.isFinished()) {
mScroller.abortAnimation(); //內(nèi)部滾動(dòng)完后,立即停止
}
} else {
distanceRemain = -distance;//負(fù)數(shù)
distance = 0;
}
} else {
if (child0Top + distance > 0) {//child0放大,child1移動(dòng)
int off = (int) (child0Top + distance);
distance = -child0Top;
if (null != child0View) {
child0View.getLayoutParams().height += off;
requestLayout();
} else {
off = 0;
}
child1View.offsetTopAndBottom(off);
if (null != mTailView) {
mTailView.offsetTopAndBottom(off);
}
}
}
}
if (0 != (int) distance) {
if (null != child0View) {
child0View.offsetTopAndBottom((int) distance);
}
child1View.offsetTopAndBottom((int) distance);
if (null != mTailView) {
mTailView.offsetTopAndBottom((int) distance);
}
requestLayout();//奇酷360這里必須調(diào)用, 否則顯示有點(diǎn)問(wèn)題
}
scrollByForMidView(distanceRemain);//外部無(wú)法滾動(dòng)的時(shí)候,內(nèi)部滾動(dòng)
if (!mPullRefreshLoading && !mLoadingMore) {
int tailviewTop = null != mTailView ? mTailView.getTop() : getHeight();//注意默認(rèn)值
if (tailviewTop < getHeight() && mHasLoadMore) {//加載更多
mLoadingMore = true;
if (mRefreshLoad != null) {
mRefreshLoad.pullUpStartLoadMore();
}
} else {
if (mHasPullRefresh) {
if (distance < 0) {
int child0Bottom = child0View.getBottom();
if (child0Bottom < mCanRefreshHeight) {
mTvRefreshInfo.setText("下拉刷新");
}
} else {
int child0Bottom = child0View.getBottom();
if (child0Bottom > mCanRefreshHeight) {
mTvRefreshInfo.setText("松開(kāi)刷新");
}
}
}
}
}
}
處理標(biāo)準(zhǔn)控件小距離滾動(dòng)代碼,這里L(fēng)istView有點(diǎn)特殊。
private void scrollByForMidView(float distanceRemain) {
if (0 != (int) distanceRemain) {
View child1View = getChildAt(null == mHeadView ? 0 : 1);
if (child1View instanceof ListView) {
((ListView) child1View).smoothScrollBy((int) distanceRemain, 0);
} /*else if (child1View instanceof ScrollView){
((ScrollView) child1View).smoothScrollBy(0,(int) distanceRemain);
}*/ else {
child1View.scrollBy(0, (int) distanceRemain);
}
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android LinearLayout和RelativeLayout組合實(shí)現(xiàn)精確布局方法介紹
- Android應(yīng)用借助LinearLayout實(shí)現(xiàn)垂直水平居中布局
- Android中LinearLayout布局的常用屬性總結(jié)
- android LinearLayout 布局實(shí)例代碼
- Android App中的多個(gè)LinearLayout嵌套布局實(shí)例解析
- android中圖片翻頁(yè)效果簡(jiǎn)單的實(shí)現(xiàn)方法
- 解析Android中實(shí)現(xiàn)滑動(dòng)翻頁(yè)之ViewFlipper的使用詳解
- Android利用懸浮按鈕實(shí)現(xiàn)翻頁(yè)效果
- Android通過(guò)手勢(shì)實(shí)現(xiàn)答題器翻頁(yè)效果
- 基于Android實(shí)現(xiàn)3D翻頁(yè)效果
- android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼
- Android編程使用LinearLayout和PullRefreshView實(shí)現(xiàn)上下翻頁(yè)功能的方法
相關(guān)文章
android非RxJava環(huán)境下使用Handler實(shí)現(xiàn)預(yù)加載
這篇文章主要介紹了android非RxJava環(huán)境下使用Handler實(shí)現(xiàn)預(yù)加載的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
flutter 屏幕尺寸適配和字體大小適配的實(shí)現(xiàn)
這篇文章主要介紹了flutter 屏幕尺寸適配和字體大小適配的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
Android 分析實(shí)現(xiàn)性能優(yōu)化之啟動(dòng)速度優(yōu)化
在移動(dòng)端程序中,用戶(hù)希望的是應(yīng)用能夠快速打開(kāi)。啟動(dòng)時(shí)間過(guò)長(zhǎng)的應(yīng)用不能滿(mǎn)足這個(gè)期望,并且可能會(huì)令用戶(hù)失望。輕則鄙視你,重則直接卸載你的應(yīng)用2021-11-11
Android實(shí)現(xiàn)淘寶底部圖標(biāo)導(dǎo)航欄
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)淘寶底部圖標(biāo)導(dǎo)航欄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Kotlin淺析延遲初始化與密封類(lèi)的實(shí)現(xiàn)方法
Kotlin語(yǔ)言的許多特性,包括變量不可變,變量不可為空,等等。這些特性都是為了盡可能地保證程序安全而設(shè)計(jì)的,但是有些時(shí)候這些特性也會(huì)在編碼時(shí)給我們帶來(lái)不少的麻煩,下面我們來(lái)了解延遲初始化和密封類(lèi)的特點(diǎn)2022-08-08
Android編程實(shí)現(xiàn)文字倒影效果的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)文字倒影效果的方法,涉及Android布局與圖形繪制相關(guān)操作技巧,需要的朋友可以參考下2017-03-03
Android實(shí)現(xiàn)瘋狂連連看游戲之開(kāi)發(fā)游戲界面(二)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)瘋狂連連看游戲之開(kāi)發(fā)游戲界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android實(shí)現(xiàn)簡(jiǎn)單的答題系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單的答題系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01

