Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發技術學習之下拉刷新功能的實現

Android開發技術學習之下拉刷新功能的實現

編輯:關於Android編程

好久沒有寫博客了,最近都在忙。有時候即使是有時間也會很懶,就會想玩一玩,放松放松!一直都沒有什麼時間更新我這個菜鳥的博客了。不過今天不一樣,我要給大家講講怎麼實現許多app中下拉刷新的功能。比如騰訊的QQ、新浪微博等等。為什麼我會寫這篇技術博客,是因為我熱愛技術,平時喜歡學習一些比較好玩的demo。故而有了這篇技術博客的誕生。好了,廢話不多說了,下面開始進入正題吧! 下拉刷新的功能,相信大家一定都接觸過。比如一個界面上顯示了一些內容,你只要按住手機的屏幕下拉一定的距離就可以實現刷新的功能,當然你也可以下拉一段距離後抬起你的手,此時就不會刷新了,繼續回到界面上。 首先需要你新建一個項目,默認有MainActivity和activity_main.xml布局。且activity_main.xml布局如下所示: activity_main.xml布局 該布局的代碼很簡單,就引用到自定義的一個view,在項目中的名字叫做MyListView,該類繼承了ListView,需要重寫構造方法。MyListView類的代碼如下所示: -
package com.example.zq.pullfresh;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by zq on 2016/4/26.
 */
public class MyListView extends ListView {

    private final View viewHeader;
    private static final byte DONE = 1;
    private static final byte PULL = 2;
    private static final byte RELEASE = 3;
    private static final byte RELEASE_NOT_FRESHING = 4;
    private byte currentState;
    private final int height;
    private TextView tvState, tvTime;
    private ImageView ivArrow;
    private ProgressBar pBar;
    private int downY, moveY, showY;
    private onRefreshingListener onRefreshingListener;

    // TODO: 2016/4/26 構造方法
    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO: 2016/4/26 構建下拉刷新的布局
        viewHeader = View.inflate(context, R.layout.layout_header, null);
        setViews(viewHeader);// TODO: 2016/4/26 控件初始化
        this.addHeaderView(viewHeader);// TODO: 2016/4/26 添加頭部刷新布局
        viewHeader.measure(0, 0);// TODO: 2016/4/26 精確測量
        height = viewHeader.getMeasuredHeight();// TODO: 2016/4/26 獲取布局的高度
        viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隱藏刷新布局
        String strCurrentDate = GetCurrentDate();
        tvTime.setText(strCurrentDate);// TODO: 2016/4/26 設置系統當前時間
        currentState = DONE;
    }

    private void setViews(View viewHeader) {
        tvState = (TextView) viewHeader.findViewById(R.id.tv_state);// TODO: 2016/4/26 下拉狀態
        tvTime = (TextView) viewHeader.findViewById(R.id.tv_updateTime);// TODO: 2016/4/26 刷新時間
        ivArrow = (ImageView) viewHeader.findViewById(R.id.iv_arrow);// TODO: 2016/4/26 下拉時箭頭
        pBar = (ProgressBar) viewHeader.findViewById(R.id.progressBar);// TODO: 2016/4/26 進度條
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:// TODO: 2016/4/26 手勢按下
                if (currentState == DONE) {
                    currentState = PULL;
                    downY = (int) ev.getY();// TODO: 2016/4/26 獲取按下時的Y坐標
                }
                break;
            case MotionEvent.ACTION_MOVE:// TODO: 2016/4/27 手勢移動
                if (currentState == PULL) {
                    moveY = (int) ev.getY();// TODO: 2016/4/26 移動時的Y坐標
                    showY = moveY - downY + (-height);// TODO: 2016/4/26 獲取下拉顯示的長度
                    viewHeader.setPadding(0, showY, 0, 0);//// TODO: 2016/4/26 下拉的時候慢慢的將布局顯示出來
                    // TODO: 2016/4/26 判斷向下拉了很久
                    if (showY > height) {
                        ivArrow.setImageResource(R.mipmap.ic_launcher);// TODO: 2016/4/26 將下拉箭頭設置為上拉箭頭
                        tvState.setText("松開刷新");
                        currentState = RELEASE;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:// TODO: 2016/4/27 手勢抬起
                // TODO: 2016/4/26 下拉了一段距離但是沒有刷新的情況
                if (showY < height) {
                    ivArrow.setImageResource(R.drawable.arrow);// TODO: 2016/4/26 設置下拉箭頭
                    ivArrow.setVisibility(View.VISIBLE);// TODO: 2016/4/26 設置下拉箭頭可見
                    pBar.setVisibility(View.GONE);
                    tvState.setText("下拉刷新");
                    viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隱藏下拉刷新布局
                    currentState = DONE;
                }
                if (currentState == RELEASE) {
                    tvState.setText("正在刷新");
                    pBar.setVisibility(View.VISIBLE);
                    ivArrow.setVisibility(View.GONE);
                    String strCurrentDate = GetCurrentDate();
                    tvTime.setText(strCurrentDate);// TODO: 2016/4/26 設置刷新時間,為系統當前時間
                    // TODO: 2016/4/26 比如聯網獲取數據,可以讓別人調接口
                    if (onRefreshingListener != null) {
                        // TODO: 2016/4/27 主界面回調該方法,但在主界面對該方法做具體的實現
                        this.onRefreshingListener.onRefreshing(this);
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    // TODO: 2016/4/26 獲取系統當前的日期 格式為:yyyy-MM-dd HH:mm:ss
    private String GetCurrentDate() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Date currentData = new Date(System.currentTimeMillis());//// TODO: 2016/4/26 獲取系統當前時間
        String strDate = format.format(currentData);// TODO: 2016/4/26 格式化系統當前時間
        return strDate;// TODO: 2016/4/26 返回格式化後的日期
    }

    // TODO: 2016/4/26 接收接口實現類
    public void setOnRefreshingListener(onRefreshingListener onRefreshingListener) {
        this.onRefreshingListener = onRefreshingListener;
    }


    public void completeRefresh() {
        ivArrow.setImageResource(R.drawable.arrow);
        ivArrow.setVisibility(VISIBLE);
        pBar.setVisibility(View.GONE);
        tvState.setText("下拉刷新");
        viewHeader.setPadding(0, -height, 0, 0);// TODO: 2016/4/26 隱藏下拉刷新布局 
        currentState = DONE;// TODO: 2016/4/26 用於重復刷新
    }
}
代碼很簡單,就是自定義了一個view,MyListView繼承ListView,重寫構造方法。在構造方法中,構建下拉刷新的布局,在代碼中構建的布局為viewHeader,即下拉刷新的布局;下拉刷新的xml布局如下所示:

下拉刷新布局圖

xml的代碼為:

 
          
          

          

          <framelayout android:layout_alignparentleft="true" android:layout_centervertical="true" android:layout_height="wrap_content" android:layout_width="wrap_content">

              

              

              

              
          </framelayout>

          

          

              

              
              
              
              
          

代碼很簡單,就是一個線性布局裡面放置兩個布局,兩個布局再分別放置一些控件。其中第一個布局用的是幀布局,裡面有一個下拉箭頭的圖片和一個進度條,此時需要將進度條隱藏,設置android:visibility=”gone”就可以了。第二個布局用的是一個線性布局,裡面有兩個TextView控件,一個是用於當前狀態提示顯示,另一個是用於最近更新提示顯示。在MyListView類的構造方法中將下拉刷新布局構建好了之後,進行控件的一些初始化操作,就可以將該布局添加到頭部了,this.addHeaderView(viewHeader);添加到頭部的時候需要精確的測量布局的高度。高度測量完成後,設置padding,可以隱藏下拉刷新的布局,viewHeader.setPadding(0,-height, 0, 0);這裡為什麼要將top參數設置為-height,因為設置在頭部需要將其隱藏。一些當前狀態提示和時間提示的賦值我就不多說了,大家都看的懂,只要給對應的控件設置你需要讓其顯示的內容就可以了。下面開始關鍵的代碼講解:在MyListView類中重寫了屏幕的觸摸事件,為什麼要重寫該方法,因為下拉刷新這個功能是對手勢做判斷,執行相應操作實現的。如圖方法:

重寫屏幕觸摸事件方法

即重寫public boolean onTouchEvent(MotionEvent ev) { ......
return super.onTouchEvent(ev); }
方法。

在該方法中,首先獲取手勢動作action,手勢動作有三個:手勢按下,手勢移動,手勢抬起。

當手勢按下的時候,currentState賦值為PULL,獲取按下時的Y坐標。currentState是用於在不同的手勢之間切換的一個參數標記。此時按下就執行以上操作就可以。 接下來需要下拉移動了,手勢動作也相應的變成了手勢移動。在手勢移動的時候,判斷currentState是否為PULL,為PULL的話,就獲取移動時的Y坐標,根據手勢按下時的Y坐標和移動時的Y坐標獲取下拉顯示的長度showY。獲取的代碼是:showY = moveY - downY + (-height);將下拉的布局慢慢顯示出來就設置padding的tZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcM6qtbHHsLvxyKG1xM/CwK3P1Mq+tcSzpLbIoaPV4tH5xOPSxravtcTKsbryvs2/ydLUwv3C/bXEz9TKvrP2wLTBy6Gj1eLKsdTZxdC2z8rHt/HP8s/Cz8LArcHLuty+w6OsyOe5+8rHtcS7sKOsvs29q8/CwK28/c23aXZBcnJvd8no1sPOqsnPwK28/c23o6zTydPaw7vT0MnPwK28/c23tcTNvMas18rUtKOsztK+zdPDaWNfbGF1bmNoZXK0+szmwcuho7Wxx7DXtMysz9TKvszhyr50dlN0YXRlvs3J6NbDzqombGRxdW87y8m/qsui0MImcmRxdW87o6zX7rrzvatjdXJyZW50U3RhdGW4s9a1zqpSRUxFQVNFoaMgytbKxtLGtq++zb3hyvjBy6OstMvKscrWysbMp8bwtcS2r9f3vs3AtMHLo6zK1srGzKfG8LXEyrG68rfWwb3W1sfpv/aho7Xa0rvW1s/CwK3Su7bOvuDA67WrysfDu9PQy6LQwrXEx+m/9qO7we3Su9bWvs3Kx8rNt8XLotDCtcTH6b/2oaPPwsCt0ru2zr7gwOu1q8O709DLotDCtcTH6b/208PM9bz+JnJkcXVvO3Nob3dZICZsdDsgaGVnaHQmcmRxdW87wLTF0LbPo6zI57n7uMPM9bz+s8nBoqOsvs3LtcP3xOO0y8qxxOPPwru51NrPwsCto6zPwsCtw7vT0L3hyvijrLTLyrHLyb+qo6y8tMrWysbMp8bwtcTKsbryo6zPwsCty6LQwrK8vta+zbvh1NnSu7TOtcTS/rLYoaPT0MjLu+HOytXiwO/Oqsqyw7TTw3Nob3dZICZsdDsgMMC01/bF0LbPo7+1sbK8vta41brDyKuyv8/Uyr6z9sC0tcTKsbryvs3M4cq+xOO/ydLUy6LQwsHLsrvKx7j8usPC8KO/yOe5+8Tj1eLR+czhzsqjrLmnz7LE46OsztLOqsqyw7TV4tH50LTE49OmuMO2rsHLo6zO0ta7ysfIw8bkz8LArbXEvuDA69TZs6TSu7Xj1NnWtNDQy6LQwrLZ1/eho9a70qrKx7Tz09ow0tTJz7XEyv3Wtb7Nv8nS1KOsv7TE47j2yMu1xM+yusPBy6GjILb4ys23xcui0MK1xMfpv/bTw8z1vP5jdXJyZW50U3RhdGUgPT0gUkVMRUFTRcC0xdC2z6OstbG0y8z1vP6zycGio6y8tMrWysbSxravveHK+Lrzo6jK1srG0sa2r8qxz8LArcHL1+O5u7OktcS+4MDro6mjrLj4tbHHsNe0zKzP1Mq+zOHKvnR2U3RhdGXJ6NbDzqombGRxdW871f3U2sui0MImcmRxdW87o6y9+LbIzPXJ6NbDzqrP1Mq+o6zPwsCtvP3Nt8no1sPOqrK7v8m8+6GjtbHHsMui0MLKsbzkdHZUaW1lyejWw86qz7XNs7Wxx7DKsbzko6zV4rj2uty88rWlo6y0+sLr09DXosrNo6y087zS06a4w7a8xNy2rqGjvdPPwsC0vs3Kx7vxyKHK/b7dtcTKsbrywcujrMui0MLKx86qwcu4ycqyw7SjrMrHzqrBy7vxyKHSu9CpzfjC58nPtcTK/b7doaO52Lz8tcS12Le9wLTBy6Os1eLA78Tjv8nS1NC0uPa907/ao6zIw7HwyMu72LX3xOO907/atcS3vbeoo6zU2rHwyMvEx8DvttS4w7e9t6jX9r7fzOW1xMq1z9aho7j4tPO80su1vt/M5bXjsMmjob/JxNzT0LXEyMuyu8rHuty2rqGjILXa0ruyvaO6tLS9qNK7uPa907/ao6zI58/CzbzL+cq+o7ogPGltZyBhbHQ9"創建的接口" src="http://img.blog.csdn.net/20160427135313298" title="\" /> 第二步:在MyListView類寫一個方法用於接收接口實現類,方法名稱為:setOnRefreshingListener(onRefreshingListener onRefreshingListener)該方法用於接收一個接口參數,也就是剛剛定義的接口。如下圖所示: * 接收接口實現類 第三步:在釋放刷新,比如聯網獲取數據,可以讓別人調你的接口,如下圖所示: * 調接口獲取數據 第四步:在MainActivity中用MyListView對象調用接收接口實現類方法,當調用該方法後需要執行接口中的onRefreshingListener.onRefreshing()方法,也就相當於在MainActivity中回調了第三步當中提到的方法this.RefreshingListener.onRefreshing(this)。但是具體的操作還是在MainActivity中實現。這其實也就是android中接口的回調機制,如果還不是很懂的就自己去百度“接口回調機制 ”,弄清楚它。 在MainActivity中具體的實現方式如下圖所示: 具體實現方法 以上圖片中我標記了4處重要的地方,第一處是讓線程休眠1s,模擬獲取數據的操作;第二處是往集合中添加獲取的數據,我這裡也只用了簡單的添加方式;第三處是adapter更新數據源的變化,因為往集合中添加了數據,所以執行該方法可以更新adapter中內容。但是更新UI的操作需要執行在主線程,故runOnUiThread(…);第四處是執行方法myListView.completeRefresh();用於完成數據的刷新操作。此時又回到了MyListView這個類中,在該類中去實現該方法。主要是聯網完成了,隱藏下拉布局。如下圖所示: 聯網完成,隱藏下拉布局 代碼很簡單,就是一些圖片的顯示與進度條的隱藏,布局的設置padding等,這裡就不多說了。代碼裡都有給出。 MainActivity中的代碼如下所示:
package com.example.zq.pullfresh;

import android.support.v7.app.AppCompatActivity; import android.os.Bundle;

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

public class MainActivity extends AppCompatActivity {

private List lists = new ArrayList<>();// TODO: 2016/4/26 聲明一個集合保存數據 private MyListView myListView;

@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // TODO: 2016/4/26 設置該Activity的使用布局
    setContentView(R.layout.activity_main);
    lists.add("a");// TODO: 2016/4/26 象征性的添加幾個數據 
    lists.add("b");
    lists.add("c");
    // TODO: 2016/4/26 創建adapter
    final MyAdapter adapter = new MyAdapter(MainActivity.this, lists);
    // TODO: 2016/4/26 獲取自定義的listView
    myListView = (MyListView)findViewById(R.id.myListView);
    // TODO: 2016/4/26 設置adapter
    myListView.setAdapter(adapter);

    myListView.setOnRefreshingListener(new onRefreshingListener() {
        @Override
        public void onRefreshing(final MyListView myListView) {
            new Thread(){
                @Override
                public void run() {
                    super.run();
                    try {
                        Thread.sleep(1000);
                        lists.add(new Random().nextInt(100)+"聯網獲取的數據");
                        // TODO: 2016/4/26 主線程執行更新UI操作必須在主線程
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                adapter.notifyDataSetChanged();
                                // TODO: 2016/4/26 聯網完成了,隱藏下拉刷新布局
                                myListView.completeRefresh();
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }); } }
代碼很簡單,獲取MyListVIew對象,給對象設置adapter。這樣就可以了。獲取adapter只需要創建MyAdapter類的對象可以獲取。MyAdapter類代碼如下:
package com.example.zq.pullfresh;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

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

/**
 * Created by zq on 2016/4/26.
 */
public class MyAdapter extends BaseAdapter {

    private List lists = new ArrayList<>();
    private Context context;

    public MyAdapter(Context context, List lists) {
        this.lists = lists;
        this.context = context;
    }

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

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

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView tvData = new TextView(context);// TODO: 2016/4/26 新建一個TextView
        tvData.setText(lists.get(position));// TODO: 2016/4/26 設置數據
        tvData.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);// TODO: 2016/4/26 設置textView在listView中的字體顯示位置
        return tvData;// TODO: 2016/4/26 返回tvData
    }
}
代碼簡單,繼承BaseAdapter,重寫相關方法,getView()方法中新建一個TextView,設置數據,返回TextView對象即可。 好了,到這裡就結束了。 最後給大家顯示一些演示的demo圖片: 初始界面 下拉刷新手勢移動 松開刷新 正在刷新 完成刷新 -

每天進步一點點!加油!

源碼下載地址:安卓開發技術之下拉刷新的實現

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved