Reputation: 3520
I have a very simple layout but when I call setRefreshing(true)
in onActivityCreated()
of my fragment, it does not show initially.
It only shows when I do a pull to refresh. Any ideas why it isn't showing up initially?
Fragment 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>
Fragment code:
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
}
}
Upvotes: 146
Views: 59565
Reputation: 6680
With Android support library 24.2.0 the issue should have been solved! https://developer.android.com/topic/libraries/support-library/revisions.html#24-2-0-bugfixes
Upvotes: 14
Reputation: 637
My solution is to override 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;
}
}
}
Upvotes: 49
Reputation: 12615
These are the old workarounds.
That used to work on earlier version of the android.support.v4
, but from version 21.0.0 ongoing it doesn't work and still exists with android.support.v4:21.0.3
released at 10-12 December, 2014 and this is the reason.
SwipeRefreshLayout indicator does not appear when the setRefreshing(true)
is called before the SwipeRefreshLayout.onMeasure()
Workaround:
calling setProgressViewOffset()
on the SwipeRefreshLayout
that invalidtes the circle view of the layout causing SwipeRefreshLayout.onMeasure()
to be called immediately.
mSwipeRefreshLayout.setProgressViewOffset(false, 0,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mSwipeRefreshLayout.setRefreshing(true);
UPDATE Better Workaround
Because the actionbar could got thinner when orientation changes or you have set actionbar size manually. We set the offset in pixels from the top of this view at which the progress spinner should come to reset after a successful swipe gesture to the current actionbar size.
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));
UPDATE Nov 20 2014
If it's not very crucial to your app to show the SwipeRefreshLayout once the view is started. You can just post it to a time in the future by using handlers or any thing you want.
as an example.
handler.postDelayed(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
}, 1000);
or as Volodymyr Baydalka's answer mentioned.
Here is the issue in the android issue tracker. Please upvote it to show them that we need it to be fixed.
Upvotes: 102
Reputation: 1839
@niks.stack In response to his answer, I would show the progress wheel after onLayout()
has been done. When I used it directly after onMeasure()
, it wouldn't honor some of the offsets, but using it after onLayout()
did.
@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;
}
}
Upvotes: 1
Reputation: 94
Also you can call this method before setRefreshing..
swipeRefreshLayout.measure(View.MEASURED_SIZE_MASK,View.MEASURED_HEIGHT_STATE_SHIFT);
swipeRefreshLayout.setRefreshing(true);
It workef for me.
Upvotes: 2
Reputation: 1
Im using 'com.android.support:appcompat-v7:23.1.1'
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
getData();
}
});
previously I was using swipeRefreshLayout.setRefreshing(true);
inside getData()
method,so it wasn't working.
I don't know why its not working inside method.
Although I used swipeRefreshLayout.setRefreshing(true);
only once in my Fragment.
Upvotes: 0
Reputation: 4832
In addition to Volodymyr Baydalka use the following code as well:
swipeContainer.post(new Runnable() {
@Override
public void run() {
swipeContainer.setRefreshing(false);
}
});
Explanation
I implemented the solution given by Volodymyr Baydalka (using fragments) but after the swipeReferesh started it never went away even on calling swipeContainer.setRefreshing(false);
so had to implement the code given above which solved my problem.
any more ideas why this happens are most welcome.
Regards,
'com.android.support:appcompat-v7:22.2.1'
Upvotes: 1
Reputation: 2119
I used AppCompat Library com.android.support:appcompat-v7:21.0.3
, using your same approach and it worked. So, you update the version of that library.
Advice: RelativeLayout
doesn't support orientation, it's an attribute for 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);
}
Layout 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>
Upvotes: 1
Reputation: 3572
Based on Volodymyr Baydalka's answer, this is just a small idea that will help you to keep code clean. It deserves a post I believe: you will be able to revert the behavior easily from posting to direct method call, once the bug is fixed.
Write a utility class like:
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);
}
});
}
}
In your code substitute mSwipeContainer.setRefreshing(isRefreshing)
by Utils.setRefreshing(mSwipeContainer, isRefreshing)
: now only one point in the code needs to be changed once the bug is fixed, the Utils
class. The method can also be inlined then (and removed from Utils
).
There will be usually no noticeable visual difference. Keep in mind though that the pending refresh could keep alive your old Activity
instances, holding on the SwipeRefreshLayout
in their view hierarchies. If that is a concern, tweak the method to use WeakReference
s, but normally you don't block the UI thread anyway, and thus delay gc by a couple of milliseconds only.
Upvotes: 4
Reputation: 358
mRefreshLayout.getViewTreeObserver()
.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mRefreshLayout
.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
mRefreshLayout.setRefreshing(true);
}
});
Upvotes: 20
Reputation: 9
Another workaround is to create a new control and deriver from SwipeRefreshLayout. Override the OnMeasure function and toggle the refresh again, if refresh is activated. I use this solution in a xamarin project and it works well. Here some sample C#-Code:
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;
}
}
}
}
Upvotes: -1
Reputation: 350
My solution (without support 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);
Upvotes: 0
Reputation: 3645
Faced with same issue. My solution -
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
Upvotes: 310