【问题标题】:SwipeRefreshLayout setRefreshing() not showing indicator initiallySwipeRefreshLayout setRefreshing() 最初不显示指示器
【发布时间】:2015-01-07 15:15:37
【问题描述】:

我有一个非常简单的布局,但是当我在片段的onActivityCreated() 中调用setRefreshing(true) 时,它最初没有显示。

它只在我拉动刷新时显示。任何想法为什么它最初没有出现?

片段 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
    }
}

【问题讨论】:

  • 你用的是什么版本?
  • 编译“com.android.support:appcompat-v7:21.0.0”
  • 我确认这个问题从那个版本开始就发生在我身上。早期版本对此没有任何问题。如果我有任何解决方案,我会发布。
  • 让我试试更早的版本
  • 也不适用于我的 v20。哪个版本适合您?

标签: android swiperefreshlayout


【解决方案1】:

试试这个

mSwipeRefreshLayout.setNestedScrollingEnabled(true);

【讨论】:

    【解决方案2】:

    Android 支持库 24.2.0 应该已经解决了这个问题! https://developer.android.com/topic/libraries/support-library/revisions.html#24-2-0-bugfixes

    【讨论】:

    • Ssing AndroidX 库,问题仍然存在
    【解决方案3】:

    我的解决方案是覆盖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;
            }
        }
    }
    

    【讨论】:

    • 您的解决方案的优点是保持视图偏移不变!这样,我们将继续遵循此处指定的设计模式:google.com/design/spec/patterns/…。谢谢!
    • 您能解释一下这是如何工作的吗?它正在工作,但我不太了解内部工作原理。我只是错过了一些琐碎的事情吗?
    • setRefreshing 仅在 onMeasure 调用后有效,因此我们存储本地刷新标志,并在第一次 onMeasure 调用时应用它
    • 我喜欢这个解决方案,因为它意味着调用 SwipeRefreshLayout 的代码可以完全按照我们的意愿进行,没有任何复杂性。它基本上修复了 SwipeRefreshLayout 中的错误。 SwipeRefreshLayout 真的应该这样实现。
    • 这个答案可能看起来不是那么简单,但它通过许多支持库版本(在我的例子中是 23.1.1)很好地解决了这个问题。根据票,这个问题在 23.2 时没有解决。 code.google.com/p/android/issues/detail?id=77712
    【解决方案4】:

    请参阅 Volodymyr Baydalka 的回答。

    这些是旧的解决方法。

    这曾经在早期版本的 android.support.v4 上工作,但从版本 21.0.0 开始,它不再工作,并且在 2014 年 12 月 10 日至 12 日发布的android.support.v4:21.0.3 中仍然存在这就是原因。

    SwipeRefreshLayout.onMeasure()之前调用setRefreshing(true)时,不会出现SwipeRefreshLayout指示符

    解决方法:

    SwipeRefreshLayout 上调用setProgressViewOffset() 会使布局的圆形视图无效,从而导致立即调用SwipeRefreshLayout.onMeasure()

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

    更新更好的解决方法

    因为当方向改变或您手动设置操作栏大小时,操作栏可能会变细。我们设置了从视图顶部到当前操作栏大小的成功滑动手势后进度微调器应重置的偏移量(以像素为单位)。

    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 的回答所提到的那样。

    这是 android 问题跟踪器中的 issue。请点赞,让他们知道我们需要修复它。

    【讨论】:

    • 你对delay time做了很多测试吗?我在我的Galaxy S4 上使用500。不确定这是否会在另一台设备上出现问题。
    • 我没有对延迟时间做太多测试,但我认为500 会做得很好。我已经在emulator 上测试过了。我只是想成为safe 1000 毫秒
    • 没有必要延迟发帖。你可以发帖。毫不拖延地发布只是意味着“一旦你完成了你现在正在做的事情,就做这件事”。它现在正在做的是测量和布置你的 UI。
    【解决方案5】:

    @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 之后的好回调!谢谢。让我恼火的是,在最初的运行中,偏移量关闭了......
    【解决方案6】:

    你也可以在setRefreshing之前调用这个方法..

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

    它对我有用。

    【讨论】:

      【解决方案7】:

      我正在使用 'com.android.support:appcompat-v7:23.1.1'

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

      以前我在getData() 方法中使用swipeRefreshLayout.setRefreshing(true);,所以它不起作用。 我不知道为什么它不能在内部方法中工作。

      虽然我在 Fragment 中只使用了一次swipeRefreshLayout.setRefreshing(true);

      【讨论】:

        【解决方案8】:

        除了 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'

        【讨论】:

          【解决方案9】:

          我使用了 AppCompat 库 com.android.support:appcompat-v7:21.0.3,使用了与您相同的方法,并且成功了。因此,您更新了该库的版本。

          建议: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>
          

          【讨论】:

            【解决方案10】:

            基于Volodymyr Baydalka's answer,这只是一个小想法,可以帮助您保持代码整洁。我相信它值得一篇文章:一旦修复了错误,您将能够轻松地将行为从发布恢复为直接方法调用。

            编写一个实用程序类,如:

            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):现在,一旦修复了错误,只需更改代码中的一点,即Utils 类。然后该方法也可以内联(并从Utils 中删除)。

            通常不会有明显的视觉差异。请记住,挂起的刷新可以使您的旧Activity 实例保持活动状态,在他们的视图层次结构中保留SwipeRefreshLayout。如果这是一个问题,请调整方法以使用WeakReferences,但通常您不会阻塞 UI 线程,因此只会将 gc 延迟几毫秒。

            【讨论】:

              【解决方案11】:
              mRefreshLayout.getViewTreeObserver()
                              .addOnGlobalLayoutListener(
                                      new ViewTreeObserver.OnGlobalLayoutListener() {
                                          @Override
                                          public void onGlobalLayout() {
                                              mRefreshLayout
                                                      .getViewTreeObserver()
                                                      .removeGlobalOnLayoutListener(this);
                                              mRefreshLayout.setRefreshing(true);
                                          }
                                      });
              

              【讨论】:

              • 这个答案是唯一一个不是黑客的答案,所以它应该被接受
              • 小事一桩:.removeGlobalOnLayoutListener 应该是 .removeOnGlobalLayoutListener
              • @mkeuch 取决于您定位的 API。如果您在 API16 下定位,则必须进行 API 版本检查,并同时使用。
              • 我更喜欢这个答案,而不是最受好评的答案,因为它更清楚为什么要使用它。
              • 我必须纠正自己,这个解决方案并不总是适合我。在某些情况下,调用 setRefreshing(false) 包裹在 onGlobalLayoutListener() 中不会关闭加载指示器。
              【解决方案12】:

              另一种解决方法是从 SwipeRefreshLayout 创建一个新控件和派生程序。如果激活了刷新,则覆盖 OnMeasure 函数并再次切换刷新。我在一个 xamarin 项目中使用了这个解决方案,它运行良好。这里有一些示例 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;
                          }
                      }
                  }
              }
              

              【讨论】:

                【解决方案13】:

                我的解决方案(不支持 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);
                

                【讨论】:

                  【解决方案14】:

                  面临同样的问题。我的解决方案 -

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

                  【讨论】:

                  • 效果很好,但我不知道为什么我们需要这样做而不是 mSwipeRefreshLayout.setRefreshing(true);
                  • 这不是一个好的解决方案/解决方法。如果用户处于飞行模式,您的 refreshLayout 将在已经启动网络请求并收到其响应后开始在自己的线程上刷新(在这种情况下失败)。在处理它以停止 refreshLayout 时,它还没有开始,所以它不起作用!换句话说,refreshLayout 将在您收到回复后开始刷新。祝你好运阻止它
                  • 我记得“帖子”是同步的,并按添加顺序运行。所以你可以添加另一个帖子来阻止它。
                  • 它的实现可能看起来最简单,但它并不好。 @niks.stack 解决方案below 更好,因为不需要对使用它的代码进行任何更改,因此当最终在支持库中修复此错误时,您只需切换回支持库 SwipeRefreshLayout
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-07-02
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-06-13
                  • 2018-02-20
                  相关资源
                  最近更新 更多