实现Material风格的滑动刷新Swipe to Refresh

来源:互联网 发布:debian ubuntu 对比 编辑:程序博客网 时间:2024/06/05 14:17

Material Design用户界面指南中非常棒的一个设计是Swipe to Refresh UI pattern。实际上你可能已经看到或者用过这种效果了。在很多热门的app中都有这种效果,比facebok、 Google Newsstand, Trello, Gmail等等。

类似于如下效果:

cat names gif

Swipe to Refresh UI非常适合于基于adapter的控件(如RecyclerView and ListView),一般它们都需要支持用户的刷新请求。关于Swipe to Refresh的实现,在KitKat版本中就有了SwipeRefreshLayout,Lollipop中对SwipeRefreshLayout做了改进,v4包含了SwipeRefreshLayout控件,我们只需做一些设置就可以了。为了方便读者,我把demo放在了github 上 下载地址

我们是在一个包含最新版本Support库的Android Studio项目中实现Swipe to Refresh,我们要做的第一件事就是将support library添加进build.gradle

1
compile'com.android.support:support-v4:21.0.+'

在新建工程向导的时候自动创建了res/layouts/activity_main.xml文件,我们将一个ListView添加进SwipeRefreshLayout控件

1
2
3
4
5
6
7
8
9
10
11
<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/activity_main_swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ListView
            android:id="@+id/activity_main_listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
        </ListView>
    </android.support.v4.widget.SwipeRefreshLayout>

注意ListView被包含在了SwipeRefreshLayout的里面。每次我们滑动ListView到SwipeRefreshLayout边缘的时候,SwipeRefreshLayout都会显示一个正在加载的图标,同时触发一个onRefresh事件。onRefresh是我们为自己list刷新数据添加的一个回调方法。


设置adapter

布局我们已经搞定,现在为列表添加数据,我们用一个简单的adapter来显示数据,数据来自于res/strings.xml文件:

1
2
3
4
5
6
7
8
9
10
11
<string-array name="cat_names">
    <item>George</item>
    <item>Zubin</item>
    <item>Carlos</item>
    <item>Frank</item>
    <item>Charles</item>
    <item>Simon</item>
    <item>Fezra</item>
    <item>Henry</item>
    <item>Schuster</item>
</string-array>

设置adapter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MainActivity extends Activity {
  ListView mListView;
  SwipeRefreshLayout mSwipeRefreshLayout;
  Adapter mAdapter;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.acivity_main);
    SwipeRefreshLayout mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_main_swipe_refresh_layout);
    mListView = findViewById(R.id.activity_main_list_view);
 mListView.setAdapter(newArrayAdapter<String>(){
    String[] fakeTweets = getResources().getStringArray(R.array.fake_tweets);
    mAdapter = newArrayAdapter<String>(this, android.R.layout.simple_list_item_1, fakeTweets)
    listView.setAdapter(mAdapter);
  });
  }
}



处理数据刷新

adapter已经设置,现在我们添加下拉刷新事件。我们会免费获得一个动画效果的加载图标,我们只需要决定ListView该做什么,这取决于SwipeRefreshLayout的OnRefreshListener 接口是如何实现的。我们用getNewCatNames模拟从webservice 中得到新数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
  public void onCreate(Bundle savedInstanceState) {
  ...
    listView.setAdapter();
    mSwipeRefreshLayout.setOnRefreshListener(newSwipeRefreshLayout.OnRefreshListener() {
      @Override
      public void onRefresh() {
            refreshContent();
          ...
  }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
  // fake a network operation's delayed response
  // this is just for demonstration, not real code!
  private void refreshContent(){
  newHandler().postDelayed(newRunnable() {
          @Override
          public void run() {
    mAdapter = newArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, getNewCatNames());
    mListView.setAdapter(mAdapter);
    mSwipeRefreshLayout.setRefreshing(false);
  });
  }
  // get new cat names.
  // Normally this would be a call to a webservice using async task,
  // or a database operation
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
 private List<String> getNewCatNames() {
        List<String> newCatNames = newArrayList<String>();
        for(int i = 0; i < mCatNames.size(); i++) {
          int randomCatNameIndex = newRandom().nextInt(mCatNames.size() - 1);
          newCatNames.add(mCatNames.get(randomCatNameIndex));
        }
        returnnewCatNames;
    }


注意上面的代码中refreshContent()方法最后一行代码setRefreshing(false);setRefreshing的作用是设置刷新加载效果的icon是否继续显示,这里使用handler做了个延时,模拟实际加载数据需要的时间,当handler的post开始执行的时候setRefreshing(false)表示加载结束,停止播放加载动画。


自定义

你可以自定义SwipeRefreshLayout的外观。setColorSchemeResources()可以改变加载图标的颜色。

先在资源文件中定义几个颜色值:

1
2
3
4
5
<resources>
    <color name="orange">#FF9900</color>
  <color name="green">#009900</color>
    <color name="blue">#000099</color>
</resources>


然后调用setColorSchemeResources(R.color.orange, R.color.green, R.color.blue); 

cat names color

SwipeRefreshLayout旋转的时候将会在这三种颜色间切换。

就如你所看到的Swipe to Refresh简化了用户请求更新数据的操作,关于SwipeRefreshLayout的更多api请查看官方文档。


注:本篇文章提供的demo编译的时候虽然用的是21版本的appcompat,但是运行还是需要在5.0的设备上,因为demo的Material主题和颜色appcompat无能为力。当然你可以如下更改一下主题,这样在4.x版本上也能运行:

demo本来的主题:

styles.xml

1
2
3
4
5
6
7
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme"parent="android:ThemeOverlay.Material.Light">
        <!-- Customize your theme here. -->
        <item name="android:colorPrimary">@android:color/holo_blue_dark</item>
    </style>
</resources>

改成:

1
2
3
4
5
6
7
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme"parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
                                                  
    </style>
</resources>




通过源码我们发现SwipeRefreshLayout中的两个重要的属性:

private MaterialProgressDrawable mProgress;

private CircleImageView mCircleView;

这两个属性正是用于实现进度动画效果的,在方法createProgressView中,我们看到mCircleView最终加入到了SwipeRefreshLayout中。

?
1
2
3
4
5
6
7
8
privatevoid createProgressView() {
        mCircleView = newCircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER/2);
        mProgress = newMaterialProgressDrawable(getContext(), this);
        mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
        mCircleView.setImageDrawable(mProgress);
        mCircleView.setVisibility(View.GONE);
        addView(mCircleView);
    }

同时我们也可以查看到CirlceImageView继承了ImageView,MaterialProgressDrawabel继承了Drawable,至此我们也就明白了下来进度动画是如何实现的了,具体的细节在不做过多赘述,可自行查看源码。

class CircleImageView extends ImageView

class MaterialProgressDrawable extends Drawable implements Animatable

具体的下拉功能实现主要是在onInterceptTouchEvent、onTouchEvent方法中完成的,在此我就不在将具体代码贴出来了,大家可自行查看。


参考博客:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1215/2166.html

源码:http://download.csdn.net/detail/iblue007/9098789
支持包:http://download.csdn.net/detail/iblue007/9098931

0 1
原创粉丝点击