【问题标题】:How to implement Android Pull-to-Refresh如何实现Android Pull-to-Refresh
【发布时间】:2011-06-02 18:42:56
【问题描述】:

在 Twitter(官方应用)等 Android 应用中,当遇到 ListView 时,可以将其拉下(释放时会弹回)以刷新内容。

我想知道在您看来,实现它的最佳方式是什么?

我能想到的一些可能性:

  1. ListView 顶部的一个项目 - 但是我认为通过 ListView 上的动画滚动回项目位置 1(基于 0)并不是一件容易的事。
  2. ListView 之外的另一个视图 - 但我需要注意在拉动时将 ListView 位置向下移动,我不确定我们是否可以检测到对 ListView 的拖动触摸是否仍然真的滚动项目列表视图。

有什么建议吗?

附:我想知道官方 Twitter 应用程序源代码什么时候发布。据说会发布,但是6个月过去了,从那以后我们就再也没听说过。

【问题讨论】:

  • 这个功能是否可以在动态创建的TableLayout中实现。请帮忙.....
  • github.com/fruitranger/PulltorefreshListView 是我的另一个实现。流畅的多点触控支持。
  • 相关:“Pull-to-refresh”: an anti UI pattern on Android 是一篇文章,它认为这个 UI应该在 Android 上使用,并引发了很多关于其适用性的讨论。
  • 有人应该让 Google 知道,因为最新版本的 Android 版 Gmail 使用了这种“反模式”。
  • Pull to refresh 是(并且已经有一段时间了)iOS 和 Android 中采用的标准模式,而且非常自然,因此这种反模式讨论已经过时。用户会期望看到它并表现得如此。

标签: android listview android-listview pull-to-refresh


【解决方案1】:

谷歌终于发布了正式版的pull-to-refresh库!

在支持库里面叫SwipeRefreshLayout,文档是here

  1. 添加SwipeRefreshLayout 作为视图的父视图,这将被视为刷新布局的拉动。 (我以ListView 为例,它可以是任何ViewLinearLayoutScrollView 等)

     <android.support.v4.widget.SwipeRefreshLayout
         android:id="@+id/pullToRefresh"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <ListView
             android:id="@+id/listView"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/>
     </android.support.v4.widget.SwipeRefreshLayout>
    
  2. 为你的类添加一个监听器

     protected void onCreate(Bundle savedInstanceState) {
         final SwipeRefreshLayout pullToRefresh = findViewById(R.id.pullToRefresh);
         pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
             @Override
             public void onRefresh() {
                 refreshData(); // your code
                 pullToRefresh.setRefreshing(false);
             }
         });
     }
    

您也可以根据需要拨打pullToRefresh.setRefreshing(true/false);

更新

Android 支持库已被弃用,并已被 AndroidX 取代。可以在here找到新库的链接。

另外,您需要在您的项目中添加以下依赖项:

implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'

你可以去Refactor>>迁移到AndroidX,Android Studio会帮你处理依赖。

【讨论】:

【解决方案2】:

那些希望为 RecyclerView 实现拉取刷新功能的人可以按照我的简单教程 How to implement Pull To Refresh for RecyclerView in Android

要导入的库

implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'

XML 代码

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipe_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

活动 JAVA 代码

import androidx.appcompat.app.AppCompatActivity;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Bundle;
import android.os.Handler;

public class MainActivity extends AppCompatActivity {

private SwipeRefreshLayout swipeRefreshLayout;

...

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       
        ...
        swipeRefreshLayout = findViewById(R.id.swipe_layout);
        initializeRefreshListener();
}

    void initializeRefreshListener() {

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // This method gets called when user pull for refresh,
                // You can make your API call here,
                // We are using adding a delay for the moment
                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if(swipeRefreshLayout.isRefreshing()) {
                            swipeRefreshLayout.setRefreshing(false);
                        }
                    }
                }, 3000);
            }
        });
    }

【讨论】:

    【解决方案3】:

    我们首先要知道什么是 android 中的 Pull to refresh layout 。我们可以在 android 中调用 pull 来刷新为 swipe-to-refresh。当您从上到下滑动屏幕时,它会根据 setOnRefreshListener 执行一些操作。

    这里是tutorial,演示了如何实现android pull to refresh。我希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      在此链接中,您可以找到著名的 PullToRefresh 视图的分支,该视图具有新的有趣实现,例如 PullTorRefreshWebViewPullToRefreshGridView,或者可以在列表底部添加 PullToRefresh

      https://github.com/chrisbanes/Android-PullToRefresh

      最好的是在 Android 4.1 中完美运行(普通的PullToRefresh 不起作用)

      【讨论】:

      • 自述文件顶部的大注释表明:该项目不再维护。无论如何,这个库是我迄今为止见过的最好的库!干得好!
      • 不再维护并不意味着它不好。仍然使用它来满足我所有的刷新需求:)
      【解决方案5】:

      我认为最简单的方法是 android 支持库提供的:

      android.support.v4.widget.SwipeRefreshLayout;

      一旦导入,您就可以将布局定义如下:

        <android.support.v4.widget.SwipeRefreshLayout
              android:id="@+id/refresh"
              android:layout_height="match_parent"
              android:layout_width="match_parent">
          <android.support.v7.widget.RecyclerView
              xmlns:recycler_view="http://schemas.android.com/apk/res-auto"
              android:id="@android:id/list"
              android:theme="@style/Theme.AppCompat.Light"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:background="@color/button_material_light"
              >
      
          </android.support.v7.widget.RecyclerView>
      </android.support.v4.widget.SwipeRefreshLayout>
      

      我假设您使用回收站视图而不是列表视图。但是,listview 仍然有效,因此您只需将 recyclerview 替换为 listview 并更新 java 代码(Fragment)中的引用。

      在您的活动片段中,您首先实现接口SwipeRefreshLayout.OnRefreshListener: 我,我

      public class MySwipeFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener{
      private SwipeRefreshLayout swipeRefreshLayout;
      
      @Override
          public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
              View view = inflater.inflate(R.layout.fragment_item, container, false);
              swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh);
              swipeRefreshLayout.setOnRefreshListener(this);
      }
      
      
       @Override
        public void onRefresh(){
           swipeRefreshLayout.setRefreshing(true);
           refreshList();
        }
        refreshList(){
          //do processing to get new data and set your listview's adapter, maybe  reinitialise the loaders you may be using or so
         //when your data has finished loading, cset the refresh state of the view to false
         swipeRefreshLayout.setRefreshing(false);
      
         }
      }
      

      希望对大众有所帮助

      【讨论】:

      【解决方案6】:

      要实现android Pull-to-Refresh,试试这段代码,

      <android.support.v4.widget.SwipeRefreshLayout
              android:id="@+id/pullToRefresh"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">
      
          <ListView
              android:id="@+id/lv"
              android:layout_width="match_parent"
              android:layout_height="match_parent" >
          </ListView>
      
      </android.support.v4.widget.SwipeRefreshLayout>
      

      活动类:

      ListView lv = (ListView) findViewById(R.id.lv);
      SwipeRefreshLayout pullToRefresh = (SwipeRefreshLayout) findViewById(R.id.pullToRefresh);
      
      
      lv.setAdapter(mAdapter);
      
      pullToRefresh.setOnRefreshListener(new OnRefreshListener() {
      
              @Override
              public void onRefresh() {
                  // TODO Auto-generated method stub
      
                  refreshContent();
      
              }
          });
      
      
      
      private void refreshContent(){ 
      
           new Handler().postDelayed(new Runnable() {
                  @Override public void run() {
                      pullToRefresh.setRefreshing(false);
                  }
              }, 5000);
      
       }
      

      【讨论】:

      • 谢谢。它真的帮助了我
      【解决方案7】:

      非常有趣的Pull-to-RefreshYalantis。 适用于 iOS 的 Gif,但您可以查看 :)

      <com.yalantis.pulltorefresh.library.PullToRefreshView
      android:id="@+id/pull_to_refresh"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      <ListView
          android:id="@+id/list_view"
          android:divider="@null"
          android:dividerHeight="0dp"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
      

      【讨论】:

        【解决方案8】:

        要获取最新的 Lollipop Pull-To Refresh:

        1. 下载最新的 Lollipop SDK 和 Extras/Android 支持库
        2. 将项目的构建目标设置为 Android 5.0(否则支持包可能会出现资源错误)
        3. 将您的 libs/android-support-v4.jar 更新到第 21 个版本
        4. 使用android.support.v4.widget.SwipeRefreshLayout 加上android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener

        详细指南可以在这里找到:http://antonioleiva.com/swiperefreshlayout/

        对于 ListView,我建议在 cmets 中阅读有关 canChildScrollUp() 的信息;)

        【讨论】:

        • 加一个只是为了提到 canChildScrollUp()。非常重要!
        【解决方案9】:

        我认为最好的库是:https://github.com/chrisbanes/Android-PullToRefresh

        适用于:

        ListView
        ExpandableListView
        GridView
        WebView
        ScrollView
        HorizontalScrollView
        ViewPager
        

        【讨论】:

          【解决方案10】:

          没有人提到像 Google Now 或 Gmail 应用程序那样显示在操作栏顶部的新型“拉动刷新”。

          有一个库ActionBar-PullToRefresh 的工作原理完全相同。

          【讨论】:

            【解决方案11】:

            请注意,在 Android 和 WP 上实施时需要解决 UX 问题。

            “为什么设计师/开发人员不应该以 iOS 应用程序的方式实现下拉刷新的一个很好的指标是,Google 和他们的团队从不在 Android 上使用下拉刷新,而在 iOS 中使用它。”

            https://plus.google.com/109453683460749241197/posts/eqYxXR8L4eb

            【讨论】:

            • 我知道这已经有一年多了,但 Gmail 应用确实使用下拉刷新。但是在 UI 方面并不完全相同,列表不会向下滚动,而是在屏幕顶部显示一个新视图。
            【解决方案12】:

            我有一个非常简单的方法来做到这一点,但现在确定它是万无一失的方法 有我的代码 PullDownListView.java

            package com.myproject.widgets;
            
            import android.content.Context;
            import android.util.AttributeSet;
            import android.view.MotionEvent;
            import android.widget.AbsListView;
            import android.widget.AbsListView.OnScrollListener;
            import android.widget.ListView;
            
            /**
             * @author Pushpan
             * @date Nov 27, 2012
             **/
            public class PullDownListView extends ListView implements OnScrollListener {
            
                private ListViewTouchEventListener mTouchListener;
                private boolean pulledDown;
            
                public PullDownListView(Context context) {
                    super(context);
                    init();
                }
            
                public PullDownListView(Context context, AttributeSet attrs) {
                    super(context, attrs);
                    init();
                }
            
                public PullDownListView(Context context, AttributeSet attrs, int defStyle) {
                    super(context, attrs, defStyle);
                    init();
                }
            
                private void init() {
                    setOnScrollListener(this);
                }
            
                private float lastY;
            
                @Override
                public boolean dispatchTouchEvent(MotionEvent ev) {
                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                        lastY = ev.getRawY();
                    } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
                        float newY = ev.getRawY();
                        setPulledDown((newY - lastY) > 0);
                        postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                if (isPulledDown()) {
                                    if (mTouchListener != null) {
                                        mTouchListener.onListViewPulledDown();
                                        setPulledDown(false);
                                    }
                                }
                            }
                        }, 400);
                        lastY = newY;
                    } else if (ev.getAction() == MotionEvent.ACTION_UP) {
                        lastY = 0;
                    }
                    return super.dispatchTouchEvent(ev);
                }
            
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    setPulledDown(false);
                }
            
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                }
            
                public interface ListViewTouchEventListener {
                    public void onListViewPulledDown();
                }
            
                public void setListViewTouchListener(
                        ListViewTouchEventListener touchListener) {
                    this.mTouchListener = touchListener;
                }
            
                public ListViewTouchEventListener getListViewTouchListener() {
                    return mTouchListener;
                }
            
                public boolean isPulledDown() {
                    return pulledDown;
                }
            
                public void setPulledDown(boolean pulledDown) {
                    this.pulledDown = pulledDown;
                }
            }
            

            您只需要在要使用此 ListView 的 Activity 上实现 ListViewTouchEventListener 并设置监听器

            我在 PullDownListViewActivity 中实现了它

            package com.myproject.activities;
            
            import android.app.Activity;
            import android.os.Bundle;
            
            /**
             * @author Pushpan
             *
             */
            public class PullDownListViewActivity extends Activity implements ListViewTouchEventListener {
            
                private PullDownListView listView;
                @Override
                protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    listView = new PullDownListView(this);
                    setContentView(listView);
                    listView.setListViewTouchListener(this);
            
                    //setItems in listview
                }
            
                public void onListViewPulledDown(){
                    Log.("PullDownListViewActivity", "ListView pulled down");
                }
            }
            

            它对我有用:)

            【讨论】:

              【解决方案13】:

              我还为 Android 实现了一个强大、开源、易于使用且高度可定制的 PullToRefresh 库。您可以按照项目页面上的文档中的说明将 ListView 替换为 PullToRefreshListView。

              https://github.com/erikwt/PullToRefresh-ListView

              【讨论】:

              • 我尝试了这里列出的所有实现,而您的实现是最好的,就简单/纯/平滑的拉动刷新实现而言(没有像 Johan 那样的点击刷新怪异)。谢谢!
              • 这可以代替标准的 ListView 与 ListActivity、ListFragment 等一起使用吗?
              • 是的,只需为 PullToRefreshListView 提供正确的 id。在 XML 中:android:id="@android:id/list"
              • 这真是太棒了!谷歌把我带到了这里,通过这些例子,你的看起来最容易实现。太棒了,谢谢你先生!
              • 非常明智的设计组件,漂亮而简单。这绝对应该是公认的答案。
              【解决方案14】:

              我在这里写了一个拉动刷新组件:https://github.com/guillep/PullToRefresh 如果列表没有项目,它会起作用,并且我已经在 >=1.6 android 手机上对其进行了测试。

              感谢任何建议或改进:)

              【讨论】:

                【解决方案15】:

                我已经尝试实现一个拉动刷新组件,它远未完成,但演示了一个可能的实现,https://github.com/johannilsson/android-pulltorefresh

                主逻辑在扩展ListViewPullToRefreshListView 中实现。 在内部,它使用smoothScrollBy(API 级别 8)控制标题视图的滚动。该小部件现已更新,支持 1.5 及更高版本,但请阅读 README 以获取 1.5 支持。

                在您的布局中,您只需像这样添加它。

                <com.markupartist.android.widget.PullToRefreshListView
                    android:id="@+id/android:list"
                    android:layout_height="fill_parent"
                    android:layout_width="fill_parent"
                    />
                

                【讨论】:

                • 嗨约翰,我已经下载了你的示例代码。它适用于 android 2.2 设备,但不适用于 2.1 设备。我认为因为它使用了仅在 2.2 或更高版本中可用的 smoothScrollBy 方法,对吗?你有什么想法将这个效果实现到 2.1 或更早版本中吗?谢谢
                • 是的,正如我在smoothScrollBy 在 API 级别 8 (2.2) 中引入的答案中所说的那样是正确的。我还没有想出一个合适的方法来为其他版本实现它,但它猜测应该可以移植 smoothScrollBy 的实现,但我想讨论应该保留在项目站点而不是堆栈溢出?跨度>
                • id 是@+id/android:list 而不是@android:id/list 是不是故意的,看起来很奇怪?该项目在我这边引发了通货膨胀错误,我目前正在检查...
                • 这是我发现的最好的拉刷新库..github.com/chrisbanes/Android-PullToRefresh。它适用于 ListViews、GridViews 和 Webviews。还实现了上拉刷新模式。
                • 如何在使用 Intellij Idea 时将项目添加到库文件夹中?有人可以帮帮我吗?
                【解决方案16】:

                如果您不希望您的程序看起来像一个强制安装到 Android 中的 iPhone 程序,那么就着眼于更原生的外观和感觉并做一些类似于 Gingerbread 的事情:

                【讨论】:

                • @Rob:我的意思是当你过度拉动时列表顶部有橙色阴影,而不是像 iPhone 上那样让列表反弹。这意味着评论(不是回答),但 cmets 不能有图像。
                • 啊,对不起,好主意。我还没有玩过姜饼,所以还没有看到效果。仍在等待谷歌将其推出到 nexus 中。
                • 我有姜饼,当列表是静态的时,橙色发光效果很好。但是下拉刷新是刷新动态列表的一个很好的 UI 机制。虽然它在 iOS 世界中很流行,但它是一种在 Android 生态系统中并不奇怪的 UI 技巧。我强烈建议您在官方 Twitter 应用程序中查看。 :)
                • 这里的原生行为是表明您已到达列表的末尾。接受这一点并执行刷新同样是非本地的,因为您没有做平台所做的事情。这意味着您更有可能让用户感到惊讶。我当然不会因为我到达列表的末尾而期望也不想刷新。下拉刷新更加直观和清晰。由于原生 twitter 应用程序使用它,我认为可以公平地说这是一个很多人都熟悉的 UI 概念。
                猜你喜欢
                • 2014-05-11
                • 1970-01-01
                • 2013-01-14
                • 2021-03-17
                • 2016-03-23
                • 1970-01-01
                • 2015-04-11
                • 1970-01-01
                • 2015-08-07
                相关资源
                最近更新 更多