怎樣實現SwipeRefreshLayout的自動刷新?類似知乎安卓版一打開頁面就自動刷新載入的效果。


看了幾個回答,都沒有回答到點子上,走了彎路.

先解釋下為什麼setRefreshing沒有作用

public void setRefreshing(boolean refreshing) {

if (refreshing mRefreshing != refreshing) {

// scale and show

mRefreshing = refreshing;

int endTarget = 0;

if (!mUsingCustomStart) {

endTarget = (int) (mSpinnerFinalOffset + mOriginalOffsetTop);

} else {

endTarget = (int) mSpinnerFinalOffset;

}

setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop,

true /* requires update */);

mNotify = false;

startScaleUpAnimation(mRefreshListener);

} else {

setRefreshing(refreshing, false /* notify */);

}

}

在onCreate里或onCreateView里調用setRefreshing,這個時候關鍵變數mOriginalOffsetTop並沒有獲取正確的值。下拉效果動畫過程中沒有顯示到正確的坐標,我們看看這個值是什麼時候獲取的,上代碼

@Override

public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (mTarget == null) {

ensureTarget();

}

if (mTarget == null) {

return;

}

mTarget.measure(MeasureSpec.makeMeasureSpec(

getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),

MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(

getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));

mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleWidth, MeasureSpec.EXACTLY),

MeasureSpec.makeMeasureSpec(mCircleHeight, MeasureSpec.EXACTLY));

if (!mUsingCustomStart !mOriginalOffsetCalculated) {

mOriginalOffsetCalculated = true;

mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight();

}

mCircleViewIndex = -1;

// Get the index of the circleview.

for (int index = 0; index &< getChildCount(); index++) {

if (getChildAt(index) == mCircleView) {

mCircleViewIndex = index;

break;

}

}

}

如代碼所見,是在onMeasure里獲取的,根據CircleView的高度來賦值的。

所以要解決的方法是讓mOriginalOffsetTop能在setRefreshing前正確賦值。比較笨的方法是在onCreate里或onCreateView手動賦值,不推薦。

正確做法是在onMeasure觸發後再調用setRefreshing。

代碼如下:

root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

root.getViewTreeObserver().removeGlobalOnLayoutListener(this);

mRefreshLayout.setRefreshing(true);

loadData(true);

}

});

其它的下拉刷新庫也有類似問題,關鍵變數需要在onMeasure里獲取。也無法顯示到正確的位置。類似其它自定義控制項需要在控制項沒有度量好,先調用涉及到相關變數的方法,都可以類似解決。


回家寫 mark

2015-9-7 20:40 更新

首先想要在界面一載入的時候出現更新效果,直接調用setRfreshing(true)是出不來效果的,這個問題我以前也遇到過,必須調用

SwipeRefreshLayout.post(new Runable(){

@Override

public void run() {

SwipeRefreshLayout.setRefreshing(true);

}

});

關閉的時候也使用

SwipeRefreshLayout.post(new Runable(){

@Override

public void run() {

SwipeRefreshLayout.setRefreshing(false);

}

});

但是如果你認為這樣就會走onRefresh方法,那你就大錯特錯了,setRefreshing(true)是不會觸發onRefresh的,必須要手動調用一次

所以在界面onCreate裡面想要立刻載入就需要這樣

SwipeRefreshLayout.post(new Runable(){

@Override

public void run() {

SwipeRefreshLayout.setRefreshing(true);

}

});

onRefresh();

2015-9-7 21:55 更新

初始化:

OnRefreshListener listener = new OnRefreshListener(){
public void onRefresh(){
//TODO
}
};
SwipeRefreshLayout mSRLayout = (SwipeRefreshLayout) findviewbyId(R.id.layout);
mSRLayout.setOnRefreshListener(listener);

onCreate中:

mSRLayout.post(new Runable(){
@Override
public void run() {
SwipeRefreshLayout.setRefreshing(true);
}
});
listener.onRefresh()


謝邀。

本來在onCreate裡面調用SwipeRefreshLayout.setRefreshing(ture,true)即可,但是,這個Api是私有的。

所以只能用反射或者手動調SwipeRefreshLayout.setRefreshing(true)+RefreshLayout.OnRefreshListener.onRefresh方法

反射代碼如下:

public static void setRefreshing(SwipeRefreshLayout refreshLayout,boolean refreshing, boolean notify){
Class& refreshLayoutClass = refreshLayout.getClass();
if (refreshLayoutClass != null) {

try {
Method setRefreshing = refreshLayoutClass.getDeclaredMethod("setRefreshing", boolean.class, boolean.class);
setRefreshing.setAccessible(true);
setRefreshing.invoke(refreshLayout, refreshing, notify);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

然後調用setRefreshing(mRefreshLayout,true,true)即可。

反射請在Proguard裡面屏蔽掉setRefreshing,代碼如下

-keepclassmembers android.support.v4.widget.SwipeRefreshLayout{
private void setRefreshing(boolean,boolean);
}

以上兩種方法注意進行延時(應該算是一個bug吧https://code.google.com/p/android/issues/detail?id=77712),參考 @賈藝馳 提供的方式 怎樣實現SwipeRefreshLayout的自動刷新?類似知乎安卓版一打開頁面就自動刷新載入的效果。 - 賈藝馳的回答。

知乎會自動緩存第一頁HTTP數據,每次打開時會把緩存先展示出來,然後再去介面請求數據,同時設置SwipeRefreshLayout的自動刷新,數據到了,把緩存替換掉,同時設置SwipeRefreshLayout.setRefreshing(false)。


SwipeRefreshLayout 不刷新的主要原因是onCreate中控制項還沒有初始化,直接使用,這個等同於Handler的,他會在View初始化完成後調用Runable的東西

swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});


SwipeRefreshLayout.setRefreshing(true);我在handler中調用這個方法無效,然後在子線程調用就崩了。何解啊?


能問一下,為什麼在刷新的回調中,請求網路數據,會引起界面的重繪呢?


我以前寫過的程序是這樣實現的,就是在onCreate函數末尾放入這句

new Handler().postDelayed(new Runnable() {
if(!offline()) {
mRefreshIndicator.setRefreshing(true);
}
}, 500); //延遲500ms執行

也可以試著這麼做一下,就是重載Activity的onWindowFocusChanged(boolean hasFocus)方法。不過我沒試過,我覺得應該也可以。

public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus) {
if(!offline()) {
mRefreshIndicator.setRefreshing(true);
}
}
}


直接調用onRefresh();就可以了


推薦閱讀:

初使用 Android Studio ,體驗是不是很不好?
學習軟體編程應該從哪裡學起?
網易雲音樂 Android 客戶端底部的播放欄是怎麼做到在各個頁面存在的?
Swift(相關構架) vs Xamarin, 那個會成為真正的廣泛接受的跨平台開發構架?
如何理解android mvp模式中的interactor?

TAG:Android開發 | Android |