PullToRefresh(下拉刷新)源码浅析

提要:本文主要介绍了android提供的原生下拉刷新控件SwipeRefreshLayout;在github上,由作者Chris Banes实现的第三方下拉刷新(上拉刷新)控件的源码浅析以及根据其源码自己实现的简单下拉刷新demo。

系统控件SwipeRefreshLayout浅析

下拉刷新(上拉刷新)是近两三年android APP比较流行的实现页面更新的控件,可以使用android.support.v4.widget包的原生控件SwipeRefreshLayout实现下拉刷新功能,本demo实现了每次执行下拉刷新后生成一个1-100之间的随机整数,效果如下:


PullToRefresh(下拉刷新)源码浅析_第1张图片
向下拉动


PullToRefresh(下拉刷新)源码浅析_第2张图片
正在刷新


PullToRefresh(下拉刷新)源码浅析_第3张图片
刷新完毕


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/swipe_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_world" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <TextView
                android:id="@+id/text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新以生成一个随机数:"
                android:textSize="23sp" />

            <TextView
                android:id="@+id/text_view_generate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#69FF0000"
                android:textSize="25sp" />
        LinearLayout>
    ScrollView>

android.support.v4.widget.SwipeRefreshLayout>

xml布局代码如上所示,在引入SwipeRefreshLayout控件时,需要写全限定类名;另外需要注意的是,该控件只能嵌套一个直接子控件,而且这个子控件必须是具有滑动功能的AdapterView(如ListView,Spinner,GridView等)

activity业务逻辑

public class MainActivity extends Activity {
    private SwipeRefreshLayout mSwipeRefreshLayout;
    private TextView mTextView;

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.text_view_generate);
        mHandler = new Handler();
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);

        mSwipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {

            @Override
            public void onRefresh() {
                // TODO Auto-generated method stub
                // 产生一个刷新效果的动画
                mSwipeRefreshLayout.setRefreshing(true);
                mHandler.postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        // 三秒后停止刷新动画
                        mSwipeRefreshLayout.setRefreshing(false);
                        // 产生1-100随机整数
                        int i = (int) (Math.random() * 100 + 1);
                        mTextView.setText(String.valueOf(i));
                    }
                }, 3000);
            }
        });
    }
}

activity代码如上所示,首先声明SwipeRefreshLayout控件并绑定setOnRefreshListener监听器,参数是一个OnRefreshListener类型的接口对象,需要实现接口中未实现的方法onRefresh,在该方法中首先调用SwipeRefreshLayout的方法setRefreshing(true)方法,表示开启动画效果,接着在Handler的postDelay方法中传入两个参数,第一个参数是一个匿名Runnable接口对象,第二个参数是一个int类型整数,本demo中传入3000,代表3秒,该方法表示动画显示的持续时长,然后在run方法中关闭动画效果,并进行相应的UI操作。

系统自带的原生下拉刷新控件SwipeRefreshLayout的优点是比较简洁,没有过多花哨的动画效果,android版chrome浏览器的下拉刷新功能便使用的该控件(chrome是我最喜欢的android浏览器),然而缺点也显而易见,那就是扩展性不够好,对于现今这个APP也看脸的时代,没有炫酷的动画已经很难满足用户的需要,另外,该控件不支持上拉刷新,如果打算实现上拉刷新和更多动画效果,需要下面介绍的第三方控件。


第三方控件PullToRefresh介绍

PullToRefresh是一个开源控件,不仅支持下拉刷新,还支持上拉刷新,而且可以利用刷新时的动画效果为用户提供有用信息(如刷新时间,手势操作提示等),很多商业级别的应用已经才用了这个控件,该控件有以下特点:

  • 支持上拉刷新、下拉刷新或同时支持上拉刷新和下拉刷新
  • 支持所有设备的动画滑动;
  • Android2.3版本以上支持整屏下拉(上拉);
  • 支持下列控件作为直接子控件
    -ListView
    -ExpandableListView
    -GridView
    -WebView
    -ScrollView
    -HorizontalScrollView
    -ViewPager
  • 当滑到底部时可以弹出信息提示
  • 支持Maven;
  • 当某个页面具有下拉刷新(上拉刷新)的功能时,给提示用户;
  • 许多自定义属性;
  • 支持ListFragment。

该控件的demo源程序可以在github上下载,地址是:https://github.com/chrisbanes/Android-PullToRefresh。下面将浅析其中的部分代码。

PullToRefresh效果

先看看该控件包含一个ListView的效果:


PullToRefresh(下拉刷新)源码浅析_第4张图片
向下拉动,显示”放开以刷新”的提示和上次刷新的时间,左边显式圆形箭头图标


PullToRefresh(下拉刷新)源码浅析_第5张图片
松手后,显示”正在载入…”,左侧的圆形箭头按钮不断转动


PullToRefresh(下拉刷新)源码浅析_第6张图片
刷新完毕后,ListView在第一行增加一条内容”Added after refresh…”


PullToRefresh(下拉刷新)源码浅析_第7张图片
当滑到底部时,Toast出”End of List!”


PullToRefresh的xml布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >



    <com.handmark.pulltorefresh.library.PullToRefreshListView
        android:id="@+id/pull_refresh_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:cacheColorHint="#00000000"
        android:divider="#19000000"
        android:dividerHeight="4dp"
        android:fadingEdge="none"
        android:fastScrollEnabled="false"
        android:footerDividersEnabled="false"
        android:headerDividersEnabled="false"
        android:smoothScrollbar="true" />

LinearLayout>

xml布局控件做了封装,控件com.handmark.pulltorefresh.library.PullToRefreshListView里面含有一个ListView。

PullToRefresh的activity

public final class PullToRefreshListActivity extends ListActivity {

    static final int MENU_MANUAL_REFRESH = 0;
    static final int MENU_DISABLE_SCROLL = 1;
    static final int MENU_SET_MODE = 2;
    static final int MENU_DEMO = 3;

    private LinkedList mListItems;
    private PullToRefreshListView mPullRefreshListView;
    private ArrayAdapter mAdapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ptr_list);

        mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);

        // Set a listener to be invoked when the list should be refreshed.
        mPullRefreshListView.setOnRefreshListener(new OnRefreshListener() {
            @Override
            public void onRefresh(PullToRefreshBase refreshView) {
                String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
                        DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);

                // Update the LastUpdatedLabel
                refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);

                // Do work to refresh the list here.
                new GetDataTask().execute();
            }
        });

        // Add an end-of-list listener
        mPullRefreshListView.setOnLastItemVisibleListener(new OnLastItemVisibleListener() {

            @Override
            public void onLastItemVisible() {
                Toast.makeText(PullToRefreshListActivity.this, "End of List!", Toast.LENGTH_SHORT).show();
            }
        });

        ListView actualListView = mPullRefreshListView.getRefreshableView();

        // Need to use the Actual ListView when registering for Context Menu
        registerForContextMenu(actualListView);

        mListItems = new LinkedList();
        mListItems.addAll(Arrays.asList(mStrings));

        mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mListItems);


        mPullRefreshListView.setOnPullEventListener(soundListener);

        // You can also just use setListAdapter(mAdapter) or
        // mPullRefreshListView.setAdapter(mAdapter)
        actualListView.setAdapter(mAdapter);
    }

    private class GetDataTask extends AsyncTask<Void, Void, String[]> {

        @Override
        protected String[] doInBackground(Void... params) {
            // Simulates a background job.
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
            }
            return mStrings;
        }

        @Override
        protected void onPostExecute(String[] result) {
            mListItems.addFirst("Added after refresh...");
            mAdapter.notifyDataSetChanged();

            // Call onRefreshComplete when the list has been refreshed.
            mPullRefreshListView.onRefreshComplete();

            super.onPostExecute(result);
        }
    }



    private String[] mStrings = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler" };
}

首先为PullToRefresh控件绑定监听器setOnRefreshListener,参数是匿名接口OnRefreshListener对象,注意,若希望同时实现上拉刷新和下拉刷新,需实现匿名接口OnRefreshListener2;在onRefresh方法中首先设置了显示刷新的时间,然后执行异步任务AsyncTask,在onPostExecute方法中为ListView增加一行,并通知Adapter刷新页面,最后调用onRefreshComplete方法完成刷新;绑定setOnLastItemVisibleListener监听器用于监听ListView滑到底部时的情况。


利用PullToRefresh实现自定义demo

利用PullToRefresh控件,实现了每次下拉刷新(或上拉刷新)后,生成一组双色球号码的demo。
效果如下所示:


PullToRefresh(下拉刷新)源码浅析_第8张图片
向下拉动,显示文字提示”放开以刷新” 左侧有Android机器人图标转动


PullToRefresh(下拉刷新)源码浅析_第9张图片
松开手,显示”正在载入…”,左侧Android机器人图标转动


PullToRefresh(下拉刷新)源码浅析_第10张图片
刷新完成,生成一组双色球随机号码


PullToRefresh(下拉刷新)源码浅析_第11张图片
向上拉动,底部提示”放开以刷新”,左侧Android机器人图标转动


PullToRefresh(下拉刷新)源码浅析_第12张图片
松开手,显示”正在载入…”,左侧Android机器人图标转动


PullToRefresh(下拉刷新)源码浅析_第13张图片
刷新完成,生成第二组双色球随机号码


xml布局

"http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <com.handmark.pulltorefresh.library.PullToRefreshListView
        xmlns:ptr="http://schemas.android.com/apk/res-auto"
        android:id="@+id/pull_refresh_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:cacheColorHint="#00000000"
        android:divider="#19000000"
        android:dividerHeight="4dp"
        android:fadingEdge="none"
        android:fastScrollEnabled="false"
        android:footerDividersEnabled="false"
        android:headerDividersEnabled="false"
        android:smoothScrollbar="true"
        ptr:ptrMode="both"
        ptr:ptrDrawable="@drawable/ic_launcher" />

注意要引用控件的全限定类名com.handmark.pulltorefresh.library.PullToRefreshListView。


activity业务逻辑

public class MainActivity extends Activity {
    private PullToRefreshListView mPullToRefresh;
    private ArrayAdapter mArrayAdapter;
    private List mList;
    private ListView mListView;

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

        mArrayAdapter = new ArrayAdapter(this,
                android.R.layout.simple_list_item_1, mList);

        mPullToRefresh = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
        mListView = mPullToRefresh.getRefreshableView();
        mListView.setAdapter(mArrayAdapter);
        // 同时实现上拉刷新和下拉刷新
        mPullToRefresh.setOnRefreshListener(new OnRefreshListener2() {

            // 实现下拉刷新
            @Override
            public void onPullDownToRefresh(PullToRefreshBase refreshView) {
                // TODO Auto-generated method stub
                new GetDataTask().execute();

            }

            // 实现下拉刷新
            @Override
            public void onPullUpToRefresh(PullToRefreshBase refreshView) {
                // TODO Auto-generated method stub
                new GetDataTask().execute();

            }
        });
    }

    private class GetDataTask extends AsyncTask {

        @Override
        protected String doInBackground(Void... params) {
            // Simulates a background job.
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            return generateData();
        }

        @Override
        protected void onPostExecute(String result) {
            mList.add(result);

            mArrayAdapter.notifyDataSetChanged();

            // Call onRefreshComplete when the list has been refreshed.
            mPullToRefresh.onRefreshComplete();

            super.onPostExecute(result);
        }

        // 生成ListView中的一行数据
        private String generateData()

        {
            Set treeSet = new TreeSet();
            do {
                treeSet.add(generateARedBall());

            } while (treeSet.size() <= 6);
            String[] _string = new String[6];
            _string = treeSet.toArray(_string);
            StringBuilder _stringBuilder = new StringBuilder();
            _stringBuilder.append("【第" + (mList.size() + 1) + "组】红:");
            for (int _i = 0; _i < 6; _i++) {
                _stringBuilder.append(_string[_i]);
                _stringBuilder.append(", ");
            }
            _stringBuilder = _stringBuilder.delete(_stringBuilder.length() - 2,
                    _stringBuilder.length() - 1);

            _stringBuilder.append("蓝:" + generateABlueBall());
            return _stringBuilder.toString();
        }

        // 生成一组红色球号码
        private String generateARedBall() {
            // TODO Auto-generated method stub
            int i = (int) (Math.random() * 99 + 1);
            if (i < 10) {
                return "0" + String.valueOf(i);
            } else {
                return String.valueOf(i);
            }

        }

        // 生成一个蓝色球号码
        private String generateABlueBall() {
            // TODO Auto-generated method stub
            int i = (int) (Math.random() * 16 + 1);
            if (i < 10) {
                return "0" + String.valueOf(i);
            } else {
                return String.valueOf(i);
            }

        }

    }

}

代码实现了上述图片所示效果,该程序通过匿名接口OnRefreshListener2对象实现了上拉刷新和下拉刷新两种手势操作,生成双色球随机数。

你可能感兴趣的:(Android高级UI)