Support Library revision 19から追加されたSwipeRefreshLayout。
Gmail等でリストを下にひっぱった時に上部にプログレスがでるあれです。
昔はさーどぱーてぃーなライブラリを使っていましたが、正式にサポートするよって形になってます。
使う上での設定などはxmlとかでSwipeRefreshLayoutを入れその中にListView等をいれる。
あとはOnRefreshListenerの設定とか色とかセットするだけ。
いろいろなブログさんなんかでも言ってますがかなり簡単になってます。
ただScrollViewのようにSwipeRefreshLayoutの中に入れれるのは一つのViewGroupのみ。
しかも配置したViewGroupを起点にしてpull判定をするので、QuickReturnのようにFrameLayoutにいろいろ入れてとかする場合などは使えません。
*FrameLayoutに反応してしまい中のListViewでpullしない。
なので似たようなUI ライブラリを使ってる場合も同じくうまく動きません。
かぶせないようなヘッダーならListViewにpaddingTopかませばいいんですが場合によっては複雑になる場合もあると思いますし、なるべく公式サポートなSwipeRefreshLayoutを併用して使いたいという場合があると思います。
なのでこれをできるようにします。
pullの判定は以下のメソッドで行ってます。
public boolean canChildScrollUp(){
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 {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}というわけで面倒かもしれませんがSwipeRefreshLayoutを継承したクラスを作り以下のように返します。
*コード内でnewする場合なら直接でもいいですがこの手のViewはxmlから設置するほうが現実的だと思うのであらかじめ修正したカスタムClassを作っといたほうが使いまわせる意味でも楽だと思います。
public class StickyRefreshLayout extends SwipeRefreshLayout {
//mTargetViewは判定に使いたいListView
private ListView mTargetView;
public StickyRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StickyRefreshLayout(Context context) {
super(context);
}
public void setListView(ListView targetView){
mTargetView=targetView;
}
@Override
public boolean canChildScrollUp() {
//mTargetViewがnullなら通常通り
if (mTargetView == null) {
return super.canChildScrollUp();
}
//マルチにしたい場合はmTargetViewをViewにしinstanceofなどで処理を分ければいい
if (mTargetView.getFirstVisiblePosition() == 0) {
View childView = mTargetView.getChildAt(0);
int top = (childView == null) ? 0 : childView.getTop();
return top < 0;
} else {
return true;
}
}
}
*コメントにも記載したのですが、上記ではListViewだけを対象にしてます。他のも対象にしたい場合はListViewをViewにしてcanChildScrollUp()内でObjectごとに処理を切り分ければいいと思います。まぁリストとスクロールビュー以外は必要ないと思いますが。
んで、あとはSwipeRefreshLayoutを上記のクラスに置き換え、対象のListViewをsetListViewしてあげればいいです。
例えば以下のようにFrameLayoutでくくったものでも動きます。
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#FFF"
android:text="ヘッダーです"
/>
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
なのでQuickReturn的な作り方もできるようになると思います。
ついでですが、昔あったpullToAttachのようにpullした際に画面が引っ張られない=プログレスだけというようなことをしたい場合、SwipeRefreshLayoutに配置したView(root)のoffsetTopAndBottomメソッドで0を返すようにしてあげれば動きません。
といっても毎度これをやるのは面倒なのでFrameLayoutなんかを継承して以下のようにしたクラスを用意しておいて使いまわしたほうが楽かも。
@Override
public void offsetTopAndBottom(int offset) {
super.offsetTopAndBottom(0);
}
*ボタンが2つありますが
・StickeyRefreshはOFFでSwipeRefreshLayoutと同じ動作になります。
・PullはOFFでプログレスのみのアニメーションになります。