SwipeRefreshLayout setRefreshing()が最初にインジケーターを表示しない


144

非常にシンプルなレイアウトですが、フラグメントを呼び出すとsetRefreshing(true)onActivityCreated()最初は表示されません。

プルして更新したときにのみ表示されます。最初に表示されない理由はありますか?

フラグメントxml:

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swipe_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

        </RelativeLayout>


    </ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>

フラグメントコード:

public static class LinkDetailsFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {

    @InjectView(R.id.swipe_container)
    SwipeRefreshLayout mSwipeContainer;

    public static LinkDetailsFragment newInstance(String subreddit, String linkId) {
        Bundle args = new Bundle();
        args.putString(EXTRA_SUBREDDIT, subreddit);
        args.putString(EXTRA_LINK_ID, linkId);

        LinkDetailsFragment fragment = new LinkDetailsFragment();
        fragment.setArguments(args);

        return fragment;
    }

    public LinkDetailsFragment() {
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mSwipeContainer.setOnRefreshListener(this);
        mSwipeContainer.setColorScheme(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
        mSwipeContainer.setRefreshing(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View rootView = inflater.inflate(R.layout.fragment_link_details, container, false);
        ButterKnife.inject(this, rootView);
        return rootView;
    }

    @Override
    public void onRefresh() {
        // refresh
    }
}

どのバージョンを使用していますか?
Ahmed Hegazy 2014

コンパイル"com.android.support:appcompat-v7:21.0.0"
thunderousNinja

この問題がそのバージョンから継続的に発生したことを確認します。以前のバージョンでは問題はありません。解決策があれば掲載します。
Ahmed Hegazy 2014

以前のバージョンを試してみましょう
thunderousNinja 2014

また、私のためにv20に取り組んでいません。どのバージョンで動作しますか?
thunderousNinja 2014

回答:


307

同じ問題に直面しました。私の解決策-

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

1
うまく機能しますが、mSwipeRefreshLayout.setRefreshing(true);の代わりにこれを行う必要がある理由がわかりません。
Cocorico


1
これは良い解決策/回避策ではありません。ユーザーが機内モードの場合、refreshLayoutは、すでにネットワーク要求を開始してその応答(この場合は失敗)を受け取った後、独自のスレッドで更新を開始します。それを処理してrefreshLayoutを停止すると、まだ開始されていないため機能しません。つまり、応答を受信した後、refreshLayoutは更新を開始します。それを止める幸運
Samer

1
私が覚えているように、「投稿」は同期していて、追加した順に実行されます。したがって、別の投稿を追加して停止することができます。
Volodymyr Baydalka 2016年

2
それはおそらく実装が最も簡単に見えますが、良くありません。以下の @ niks.stackソリューションは、それを使用するコードを変更する必要がないため、より優れています。このバグがサポートライブラリで最終的に修正されると、サポートに戻るだけです。SwipeRefreshLayout
Marcin Orlowski

100

代わりにVolodymyr Baydalkaの回答を参照してください。

これらは古い回避策です。

以前はの以前のバージョンで機能してandroid.support.v4いましたが、バージョン21.0.0以降では機能しなくなり、2014年12月10〜12日のandroid.support.v4:21.0.3リリースでも引き続き存在します。これが理由です。

SwipeRefreshLayoutインジケーターはsetRefreshing(true)SwipeRefreshLayout.onMeasure()

回避策:

呼び出しsetProgressViewOffset()SwipeRefreshLayout原因となるレイアウトのサークルビューをinvalidtesそのSwipeRefreshLayout.onMeasure()直後に呼ばれるように。

mSwipeRefreshLayout.setProgressViewOffset(false, 0,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mSwipeRefreshLayout.setRefreshing(true);

UPDATEより良い回避策

方向が変わったり、アクションバーのサイズを手動で設定したりすると、アクションバーが薄くなる可能性があるためです。このビューの上部からのオフセットをピクセル単位で設定します。この位置で、スワイプジェスチャーが正常に実行された後、進行状況スピナーが現在のアクションバーサイズにリセットされます。

TypedValue typed_value = new TypedValue();
getActivity().getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.actionBarSize, typed_value, true);
mSwipeRefreshLayout.setProgressViewOffset(false, 0, getResources().getDimensionPixelSize(typed_value.resourceId));

2014年11月20日更新

ビューが開始されたときにSwipeRefreshLayoutを表示することがアプリにとってそれほど重要でない場合。ハンドラーなどを使用して、将来のある時点に投稿することができます。

例として。

handler.postDelayed(new Runnable() {

    @Override
    public void run() {
        mSwipeRefreshLayout.setRefreshing(true);
    }
}, 1000);

またはVolodymyr Baydalkaの答えが言及したように。

ここで問題アンドロイド課題追跡では。修正する必要があることを示すには、投票してください。


で多くのテストを行いましたdelay timeか?で使用500していGalaxy S4ます。これが別のデバイスで問題になるかどうかは不明です。
theblang 2014

私は、遅延時間と多くのテストをしなかったが、私は考える500上でそれをテストしているfine.Iを行うだろうemulatorちょうどになりたいと思った.I safe1000ミリ秒
アハメド・ヘガジー

3
投稿を遅らせる必要はありません。投稿するだけです。遅滞なく投稿するということは、「今行っていることを終えたら、このことを行う」ということです。それが今行っていることは、UIの測定とレイアウトです。
オーレン、

47

私の解決策はオーバーライドすることSwipeRefreshLayoutです:

public class MySwipeRefreshLayout extends SwipeRefreshLayout {

    private boolean mMeasured = false;
    private boolean mPreMeasureRefreshing = false;

    public MySwipeRefreshLayout(final Context context) {
        super(context);
    }

    public MySwipeRefreshLayout(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!mMeasured) {
            mMeasured = true;
            setRefreshing(mPreMeasureRefreshing);
        }
    }

    @Override
    public void setRefreshing(final boolean refreshing) {
        if (mMeasured) {
            super.setRefreshing(refreshing);
        } else {
            mPreMeasureRefreshing = refreshing;
        }
    }
}

3
このソリューションの利点は、ビューのオフセットをそのまま維持できることです。このようにして、Google.com / design / spec / patterns / …で指定されたデザインパターンに準拠し続けます。ありがとう!
Igor de

これがどのように機能するか説明していただけますか?動作していますが、内部の動作は完全にはわかりません。些細なことを見逃しているだけですか?
2015

setRefreshingはonMeasure呼び出しの後でのみ機能するため、ローカルの更新フラグを保存し、最初のonMeasure呼び出しでそれを適用します
nikita.zhelonkin '28

5
このソリューションが気に入ったのは、SwipeRefreshLayoutを呼び出すコードを複雑にすることなく、望みどおりのコードにすることができるからです。基本的にはSwipeRefreshLayoutのバグを修正しました。SwipeRefreshLayoutは、実際にはこの方法で実装する必要があります。
DataGraham 2015

この答えはそれほど簡単ではないように見えるかもしれませんが、多くのサポートライブラリバージョン(私の場合は23.1.1)によって問題をうまく修正しています。チケットによると、この問題は23.2で修正されていません。code.google.com/p/android/issues/detail?id=77712
Robert

20
mRefreshLayout.getViewTreeObserver()
                .addOnGlobalLayoutListener(
                        new ViewTreeObserver.OnGlobalLayoutListener() {
                            @Override
                            public void onGlobalLayout() {
                                mRefreshLayout
                                        .getViewTreeObserver()
                                        .removeGlobalOnLayoutListener(this);
                                mRefreshLayout.setRefreshing(true);
                            }
                        });

3
この回答は、ハッキングではない唯一の回答なので、受け入れる必要があります
Heinrich

1
ほんの少しだけ:.removeGlobalOnLayoutListenerは.removeOnGlobalLayoutListenerでなければなりません
mkuech

1
@mkeuchは、ターゲティングするAPIによって異なります。API16でターゲティングする場合は、APIバージョンチェックを実行し、両方を使用する必要があります。
マルコ

なぜそれが使われるのかがよりはっきりしているので、私はこれが最も投票された答えよりも少し好きです。
Marcel Bro 2016

私は自分で修正する必要があります。この解決策が常に機能するとは限りません。場合によっては、setRefreshing(false)wrapped inを呼び出してもonGlobalLayoutListener()読み込みインジケーターが閉じないことがあります。
Marcel Bro 2016


4

Volodymyr Baydalkaの回答に基づくと、これはコードをクリーンに保つのに役立つ小さなアイデアにすぎません。それは私が信じる投稿に値します。バグが修正されると、投稿から直接メソッド呼び出しに動作を簡単に戻すことができるようになります。

次のようなユーティリティクラスを記述します。

public class Utils
{
    private Utils()
    {
    }

    public static void setRefreshing(final SwipeRefreshLayout swipeRefreshLayout, final boolean isRefreshing)
    {
        // From Guava, or write your own checking code
        checkNonNullArg(swipeRefreshLayout);
        swipeRefreshLayout.post(new Runnable()
        {
            @Override
            public void run()
            {
                swipeRefreshLayout.setRefreshing(isRefreshing);
            }
        });
    }
}

あなたのコードの代わりmSwipeContainer.setRefreshing(isRefreshing)Utils.setRefreshing(mSwipeContainer, isRefreshing):バグが修正されたら、コードの1つのポイントのみを変更する必要がありますUtils。メソッドをインライン化することもできます(およびから削除Utils)。

通常、目立った視覚的な違いはありません。ただし、保留中の更新によって古いActivityインスタンスが維持さSwipeRefreshLayoutれ、ビュー階層が保持されることに注意してください。これが問題になる場合は、メソッドを調整してWeakReferences を使用しますが、通常はとにかくUIスレッドをブロックしないため、gcを数ミリ秒だけ遅延させます。


2

また、setRefreshingの前にこのメソッドを呼び出すこともできます。

    swipeRefreshLayout.measure(View.MEASURED_SIZE_MASK,View.MEASURED_HEIGHT_STATE_SHIFT);
swipeRefreshLayout.setRefreshing(true);

それは私のために働きます。


1

私はcom.android.support:appcompat-v7:21.0.3あなたの同じアプローチを使用してAppCompat Library を使用し、それはうまくいきました。そのため、そのライブラリのバージョンを更新します。

アドバイス:RelativeLayout向きをサポートしていませんLinearLayout。これはの属性です。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
                                                 Bundle savedInstanceState) {       
  ViewGroup view = (ViewGroup) inflater.inflate(R.layout.license_fragment, 
           container, false);ButterKnife.inject(this, view);
    // Setting up Pull to Refresh
    swipeToRefreshLayout.setOnRefreshListener(this);
    // Indicator colors for refresh
    swipeToRefreshLayout.setColorSchemeResources(R.color.green, 
                R.color.light_green);
}

レイアウトXML:

<android.support.v4.widget.SwipeRefreshLayout>

<ScrollView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:paddingBottom="@dimen/activity_margin_vertical"
                    android:paddingTop="@dimen/activity_margin_vertical">

    <!-- Content -->
</ScrollView>

</android.support.v4.widget.SwipeRefreshLayout>

1

Volodymyr Baydalkaに加えて、次のコードも使用します。

swipeContainer.post(new Runnable() {
        @Override
        public void run() {
            swipeContainer.setRefreshing(false);
        }
    });

説明 Volodymyr Baydalka(フラグメントを使用)によって与えられたソリューションを実装しましたが、swipeRefereshが起動した後、呼び出しでも消えることはなかったswipeContainer.setRefreshing(false); ため、上記のコードを実装して問題を解決する必要がありました。これが起こる理由はこれ以上ありません。

よろしく、

「com.android.support:appcompat-v7:22.2.1」


1

@ niks.stack彼の答えに応えて、私は終了後にプログレスホイールを表示しonLayout()ます。の直後に使用した場合onMeasure()、一部のオフセットは考慮されませんが、後で使用した場合onLayout()

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    if (!mLaidOut) {
        mLaidOut = true;
        setRefreshing(mPreLayoutRefreshing);
    }
}

@Override
public void setRefreshing(boolean refreshing) {
    if (mLaidOut) {
        super.setRefreshing(refreshing);
    } else {
        mPreLayoutRefreshing = refreshing;
    }
}

onMeasureの後に良いコールバックを探していました!Thnx。最初の実行でオフセットがオフになっていたので、私を悩ませていました
xdbas

0

私のソリューション(サポートv7なし)-

TypedValue typed_value = new TypedValue();
getTheme().resolveAttribute(android.R.attr.actionBarSize, typed_value, true);
swipeLayout.setProgressViewOffset(false, 0, getResources().getDimensionPixelSize(typed_value.resourceId));

if(!swipeLayout.isEnabled())
     swipeLayout.setEnabled(true);
swipeLayout.setRefreshing(true);

0

私は「com.android.support:appcompat-v7:23.1.1」を使用しています

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

以前はswipeRefreshLayout.setRefreshing(true);Inside getData()メソッドを使用していたため、機能しませんでした。それがメソッド内で機能しない理由はわかりません。

swipeRefreshLayout.setRefreshing(true);は私のフラグメントで一度だけ使用しましたが。



-1

別の回避策は、SwipeRefreshLayoutから新しいコントロールと派生クラスを作成することです。OnMeasure関数をオーバーライドし、リフレッシュがアクティブ化されている場合はリフレッシュを再度切り替えます。私はxamarinプロジェクトでこのソリューションを使用しており、うまく機能します。ここにいくつかのサンプルC#コード:

class MySwipeRefreshLayout : SwipeRefreshLayout
{
    /// <summary>
    /// used to indentify, if measure was called for the first time
    /// </summary>
    private bool m_MeasureCalled;

    public MvxSwipeRefreshLayout(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {
    }

    public MvxSwipeRefreshLayout(Context context)
        : base(context)
    {
    }

    public override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        base.OnMeasure(widthMeasureSpec, heightMeasureSpec);

        if (!m_MeasureCalled)
        {
            //change refreshing only one time
            m_MeasureCalled = true;

            if (Refreshing)
            {
                Refreshing = false;
                Refreshing = true;
            }
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.