如何实现类似于新版Gmail应用中的“下拉刷新”功能

34
谷歌发布了新版Gmail应用程序,提供了一种备选的方式来处理下拉刷新。与其显示下拉的已隐藏行,Gmail在操作栏之上显示一个动画消息。该消息包含一个水平线条的动画效果。
这是否是Android SDK的标准功能?我在操作栏API中找不到任何相关说明。 enter image description here

4
请看这个链接:https://github.com/chrisbanes/Android-PullToRefresh。 - Cornholio
1
我不太明白,这个问题在其他帖子中没有得到答复......我想要使用谷歌的API,有人知道在哪里获取它吗?或者如何调用它? - LuckyMe
1
相关博客文章还参考了上面链接的chrisbanes的解决方案:http://www.androiduipatterns.com/2013/06/googles-first-pull-to-refresh-good.html - laalto
2
实际上,Github 上还有另一个 chrisbanes 的下拉刷新库:https://github.com/chrisbanes/ActionBar-PullToRefresh。@Cornholio 提供的那个已经过时了。上面的博客文章中提供的链接是正确的。 - laalto
1
现在官方API已经存在了。我已经在新的答案中提供了链接。 - Booger
显示剩余6条评论
6个回答

48

太棒了。感谢你抽出时间添加这个答案。 - Reactgular
很遗憾,SwipeRefreshLayout 的实现缺少在 ActionBar 叠加层中显示自定义消息以获取不同刷新状态的可能性。进度条也显示在 ActionBar 下面而不是里面(例如,在 Google+ 应用程序中)。 - cybergen

22

Chris Banes的ActionBar-PullToRefresh库不再维护,有什么替代品吗? - AnhSirk Dasarp
2
@AnhSirkDasarp 你可能指的是他的Android-PullToRefresh。这个(ActionBar-PullToRefresh)是一个替代品。 - laalto
Gmail中的下拉刷新已经改变,请参见:http://antonioleiva.com/swiperefreshlayout/ - Richard Lindhout

10

试试这个...对我有效。

res/layout/activity_main.xml

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.swipetorefresh.MainActivity"
tools:ignore="MergeRootFrame" />

res/layout/fragment_main.xml

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

   <ListView
      android:id="@android:id/list"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

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

MainActivity.java

  public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState == null) {
        getFragmentManager().beginTransaction()
                .add(R.id.container, new PlaceholderFragment()).commit();
    }
}

public static class PlaceholderFragment extends ListFragment implements OnRefreshListener {

    private SwipeRefreshLayout mSwipeRefreshLayout;

    private static final int LIST_ITEM_COUNT = 5;
    private int mOffset = 0;

    private ArrayAdapter<String> mListAdapter;

    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container,
                false);

        // Configure the swipe refresh layout
        mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.container);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        mSwipeRefreshLayout.setColorScheme(
                R.color.swipe_color_1, R.color.swipe_color_2,
                R.color.swipe_color_3, R.color.swipe_color_4);

        // Put the first batch of countries in the list
        mListAdapter = new ArrayAdapter<String>(
                getActivity(),
                android.R.layout.simple_list_item_1,
                android.R.id.text1,
                getCountries(mOffset));

        setListAdapter(mListAdapter);

        return rootView;
    }

    private List<String> getCountries(int offset) {
        ArrayList<String> countriesList = new ArrayList<String>();
        for(int i=0; i<LIST_ITEM_COUNT;i++){
            countriesList.add(COUNTRIES[offset+i]);
        }

        mOffset = offset + LIST_ITEM_COUNT;
        return countriesList;
    }

    @Override
    public void onRefresh() {
        // Start showing the refresh animation
        mSwipeRefreshLayout.setRefreshing(true);

        // Simulate a long running activity
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
               updateCountries();
            }
        }, 5000);
    }



    private void updateCountries() {

        // Add the next batch of countries to the list
        mListAdapter.addAll(getCountries(mOffset));

        // Signify that we are done refreshing
        mSwipeRefreshLayout.setRefreshing(false);
    }



    private static final String[] COUNTRIES = {"Afghanistan",
        "Albania", "Algeria", "American Samoa", "Andorra", "Angola",
        "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
        "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
        "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus",
        "Belgium", "Belize", "Benin", "Bermuda", "Bhutan",
        "Bolivia", "Bosnia and Herzegovina", "Botswana", "Brazil",
        "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi",
        "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands",
        "Central African Republic", "Chad", "Chile", "China",
        "Christmas Island", "Cocos (Keeling) Islands", "Colombia",
        "Comoros", "Democratic Republic of the Congo (Kinshasa)",
        "Congo, Republic of(Brazzaville)", "Cook Islands", "Costa Rica",
        "Ivory Coast", "Croatia", "Cuba", "Cyprus", "Czech Republic",
        "Denmark", "Djibouti", "Dominica", "Dominican Republic",
        "East Timor (Timor-Leste)", "Ecuador", "Egypt",
        "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia"};

        }
   }

3

Chris Banes(曾经实现了安卓最好的下拉刷新组件)也实现了类似于Gmail的下拉刷新。

你可以在这里找到它:https://github.com/chrisbanes/ActionBar-PullToRefresh

请注意,该项目仍在开发中,因此当前的API可能会发生变化。


1
fragment_main.xml

<RelativeLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testloading.MainActivity$PlaceholderFragment" >

  <ListView
      android:id="@+id/list"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

</RelativeLayout>    


counteries.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView 
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="47dp"
        android:gravity="center_vertical"
        android:textStyle="bold"/>
</LinearLayout>



package com.example.testloading;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment()).commit();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        private ListView listView;
        private static final int LIST_ITEM_COUNT = 20;
        private int mOffset = 0;
        private boolean flag_loading;

        private MyAdapter adapter;
        private List<String> list;

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container,
                    false);

            listView = (ListView) rootView.findViewById(R.id.list);
            list = getCountries(mOffset);
            adapter = new MyAdapter(list, getActivity());
            listView.setAdapter(adapter);
            listView.setOnScrollListener(new OnScrollListener() {

                @Override
                public void onScrollStateChanged(AbsListView view,
                        int scrollState) {
                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    Log.d(TAG, "firstVisibleItem : " + firstVisibleItem
                            + " , visibleItemCount : " + visibleItemCount
                            + " , totalItemCount : " + totalItemCount);
                    if (firstVisibleItem + visibleItemCount == totalItemCount) {
                        Log.d(TAG, "ZZZ offSet : " + mOffset);
                        if (COUNTRIES.length > mOffset) {
                            if (flag_loading == false) {
                                Log.d(TAG, "ZZZ inside : ");
                                flag_loading = true;
                                additems();
                            }
                        }
                    }

                }
            });
            return rootView;
        }

        protected void additems() {
            list.addAll(getCountries(mOffset));
            adapter.notifyDataSetChanged();
            listView.invalidate();
            flag_loading = false;
        }

        private List<String> getCountries(int offset) {
            ArrayList<String> countriesList = new ArrayList<String>();
            for (int i = 0; i < LIST_ITEM_COUNT; i++) {
                if (COUNTRIES.length > offset + i) {
                    countriesList.add(COUNTRIES[offset + i]);
                }
            }

            mOffset = offset + LIST_ITEM_COUNT;
            return countriesList;
        }

        private static final String[] COUNTRIES = { "Afghanistan", "Albania",
                "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla",
                "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia",
                "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas",
                "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
                "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
                "Bosnia and Herzegovina", "Botswana", "Brazil",
                "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi",
                "Cambodia", "Cameroon", "Canada", "Cape Verde",
                "Cayman Islands", "Central African Republic", "Chad", "Chile",
                "China", "Christmas Island", "Cocos (Keeling) Islands",
                "Colombia", "Comoros",
                "Democratic Republic of the Congo (Kinshasa)",
                "Congo, Republic of(Brazzaville)", "Cook Islands",
                "Costa Rica", "Ivory Coast", "Croatia", "Cuba", "Cyprus",
                "Czech Republic", "Denmark", "Djibouti", "Dominica",
                "Dominican Republic", "East Timor (Timor-Leste)", "Ecuador",
                "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
                "Estonia", "Ethiopia" };

    }
}

class MyAdapter extends BaseAdapter {

    private List<String> list;
    private LayoutInflater layoutInflater;

    public MyAdapter(List<String> list, Context context) {
        this.list = list;
        layoutInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup arg2) {

        ViewHolder viewHolder;

        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.counteries, null);
            viewHolder = new ViewHolder();
            viewHolder.textView = (TextView) convertView
                    .findViewById(R.id.text_view);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.textView.setText(list.get(position));
        return convertView;
    }

    class ViewHolder {
        public TextView textView;
    }
}

0

测试一下。 使用: swipeRefreshLayout.setRotation(180f);

在您的适配器ListView中,使用getView方法: view.setRotation(180f);

倒序排列列表中的项目。

或者直接在xml中使用android:rotation="180"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接