Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 中 ListView 的 下拉刷新 和 上拉加載 的 重點及學習(一)

Android 中 ListView 的 下拉刷新 和 上拉加載 的 重點及學習(一)

編輯:關於Android編程

大多App中的一個必備功能:用listView實現下拉刷新和上拉加載,其實有很多大牛都寫了類似的Blog,但我還想記錄一下,梳理自己的思路,而且我會想之前寫的輪播圖博客一樣,我的重點是在如何寫的思路,不願直接貼代碼,想看代碼的直接看文章最下面吧 :)


這裡寫圖片描述

如上gif動圖所示,接下來我們要完成下拉刷新的實現。



一. 完成 xml 文件的編寫 :


1.完成listView的頭布局

這裡寫圖片描述
結合了幀布局和線性布局




    <framelayout android:layout_height="50dp" android:layout_margin="5dp" android:layout_width="50dp">

        

        
    </framelayout>

    

        

        
    



2. 自定義ProgressBar

這裡寫圖片描述

這裡紅色漸變的圓圈為自定義的xml<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">



二. 完成邏輯編寫實現下拉刷新 :

1. 自定義RefreshListView繼承 Listview

繼承3個構造方法,並在init()中寫initHeaderView()方法,初始化頭布局

    public PullToRefreshListView(Context context) {
        super(context);
        init()
    }

    public PullToRefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init()
    }

    public PullToRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init()
    }

2. 初始化頭布局並隱藏

默認讓頭布局隱藏,主要就是測得我們剛寫的xml文件頭布局的高度,用padding將它隱藏起來即可,再設置listView添加頭布局

    /**
     * 初始化頭布局
     */
    private void initHeaderView() {

        mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
        mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
        pb = (ProgressBar) mHeaderView.findViewById(R.id.pb);
        mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title);
        mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh);


        // 提前手動測量寬高
        mHeaderView.measure(0, 0);// 按照設置的規則測量

        mHeaderViewHeight = mHeaderView.getMeasuredHeight();
        System.out.println(" measuredHeight: " + mHeaderViewHeight);

        // 設置內邊距, 可以隱藏當前控件 , -自身高度
        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

        // 在設置數據適配器之前執行添加 頭布局/腳布局 的方法.
        addHeaderView(mHeaderView);
    }

3. 滑動事件處理(重點)

萬事俱備,頭布局也已經隱藏掉,接下來我們想通過手指的滑動,讓頭布局顯示出來,這時就涉及到 onTouchEvent 事件。

    public static final int PULL_TO_REFRESH = 0;// 下拉刷新狀態
    public static final int RELEASE_REFRESH = 1;// 釋放刷新狀態
    public static final int REFRESHING = 2; // 刷新中狀態
@Override
    public boolean onTouchEvent(MotionEvent ev) {

        // 判斷滑動距離, 給Header設置paddingTop
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downY = ev.getY();
            System.out.println("downY: " + downY);

            break;
        case MotionEvent.ACTION_MOVE:
            moveY = ev.getY();
            System.out.println("moveY: " + moveY);
            // 如果是正在刷新中, 就執行父類的處理
            if(currentState == REFRESHING){
                return super.onTouchEvent(ev);
            }


            float offset = moveY - downY; // 移動的偏移量
            // 只有 偏移量>0, 並且當前第一個可見條目索引是0, 才放大頭部
            if(offset > 0 && getFirstVisiblePosition() == 0){

         //int paddingTop = -自身高度 + 偏移量

                int paddingTop = (int) (- mHeaderViewHeight + offset);
                mHeaderView.setPadding(0, paddingTop, 0, 0);

                if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 頭布局完全顯示
                    System.out.println("切換成釋放刷新模式: " + paddingTop);
                    // 切換成釋放刷新模式
                    currentState = RELEASE_REFRESH;
                    updateHeader(); // 根據最新的狀態值更新頭布局內容
                }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 頭布局不完全顯示
                    System.out.println("切換成下拉刷新模式: " + paddingTop);
                    // 切換成下拉刷新模式
                    currentState = PULL_TO_REFRESH;
                    updateHeader(); // 根據最新的狀態值更新頭布局內容
                }

                return true; // 當前事件被我們處理並消費
            }
            break;

        case MotionEvent.ACTION_UP:

            // 根據剛剛設置狀態
            if(currentState == PULL_TO_REFRESH){
//          - paddingTop < 0 不完全顯示, 恢復
                mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
            }else if(currentState == RELEASE_REFRESH){
//          - paddingTop >= 0 完全顯示, 執行正在刷新...
                mHeaderView.setPadding(0, 0, 0, 0);
                currentState = REFRESHING; 
                updateHeader();
            }
            break;

        default:
            break;
        }

        return super.onTouchEvent(ev);
    }

首先要了解三個狀態:

這裡寫圖片描述

(1)PULL_TO_REFRESH下拉刷新:拉的過程中會進入到“釋放刷新”,要是拉到一半就放手未進入“釋放刷新”,就不會去刷新數據。
(2)RELEASE_REFRESH釋放刷新:拉到了一定距離,只要放手就會進入”刷新中”狀態
(3)REFRESHING刷新中:狀態為正在刷新,此時就可以去請求數據了。


其次從 DownMoveUp這三個動作來分析邏輯:
(1) Down: 即手指剛剛點下去,在這裡只需要獲取當前的 y坐標值(上下滑動,只在意Y值)

(2) Move: 即手指滑動的過程,在這裡需要根據移動的距離 offset 當前的狀態顯示出頭布局改變狀態

1. 當前狀態為“正在刷新”,即不處理(也不看第二點了,return出去)。

    2.  只有 偏移量 offset>0, 並且當前第一個可見條目索引是0, 才顯示頭布局。
        paddingTop :  需要隱藏的距離= - 自身高度 + 偏移量。

        2.1 (paddingTop >= 0 且 狀態不等於“釋放刷新”):頭布局完全顯示,此時狀態改為“釋放刷新”
        2.2 (paddingTop < 0 且 狀態不等於“釋放刷新”): 頭布局不完全顯示,此時狀態改為“下拉刷新”
        2.3 其余情況不管:

情況2.1:這裡寫圖片描述

情況2.2:這裡寫圖片描述


(3) Up: 即手指抬起,根據Move過程後改變的狀態來判斷是否進行刷新的邏輯狀態修改

1.狀態為”下拉刷新“ :即收起頭布局,不需要進行刷新的邏輯。
2.狀態為”釋放刷新“ :顯示完全頭布局,修改狀態為”正在刷新“,進行刷新的邏輯。

(以上代碼中的refreshState方法留到下下一點講,其實就是一個UI的變化)
(監聽對象mListener 采取回調,第六點講解)



4.下拉刷新箭頭旋轉動畫

以上完成後,接下來完善一下頭布局的動畫,需要在init()中初始化initAnimation()

    /**
     * 初始化頭布局的動畫
     */
    private void initAnimation() {
        // 向上轉, 圍繞著自己的中心, 逆時針旋轉0 -> -180.
        rotateUpAnim = new RotateAnimation(0f, -180f, 
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateUpAnim.setDuration(300);
        rotateUpAnim.setFillAfter(true); // 動畫停留在結束位置

        // 向下轉, 圍繞著自己的中心, 逆時針旋轉 -180 -> -360
        rotateDownAnim = new RotateAnimation(-180f, -360,
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateDownAnim.setDuration(300);
        rotateDownAnim.setFillAfter(true); // 動畫停留在結束位置 
    }

5. refreshState()修改頭布局中UI的變化

第五點同第四點都是有關於UI的變化,比較簡單,雖然在onTouchevent中根據不同情況修改了 頭布局的顯示多少 ,但是頭布局中的小控件都有相應的動畫或改變,需要抽取一個方法更細致的展示。

    /**
     * 根據當前狀態刷新界面
     */
    private void refreshState() {
        switch (mCurrentState) {
        case STATE_PULL_TO_REFRESH:
            tvTitle.setText("下拉刷新");
            pbProgress.setVisibility(View.INVISIBLE);
            ivArrow.setVisibility(View.VISIBLE);
            ivArrow.startAnimation(animDown);
            break;
        case STATE_RELEASE_TO_REFRESH:
            tvTitle.setText("松開刷新");
            pbProgress.setVisibility(View.INVISIBLE);
            ivArrow.setVisibility(View.VISIBLE);
            ivArrow.startAnimation(animUp);
            break;
        case STATE_REFRESHING:
            tvTitle.setText("正在刷新...");

            ivArrow.clearAnimation();// 清除箭頭動畫,否則無法隱藏

            pbProgress.setVisibility(View.VISIBLE);
            ivArrow.setVisibility(View.INVISIBLE);
            break;

        default:
            break;
        }
    }

6.下拉刷新監聽(重點!!!!!!回調!!!!)

運用回調,使在手指抬起Up後,能夠調用方法,進行刷新的邏輯。
(1)下拉刷新的回調接口

public interface OnRefreshListener {
        public void onRefresh();
    }


(2)暴露接口,設置監聽

    public void setOnRefreshListener(OnRefreshListener listener) {
        mListener = listener;
    }


(3)定義成員變量,接收監聽對象

private OnRefreshListener mListener;


(4) 在合適的地方進行回調
(之前第三點中onTouchevent事件中進行回調)

 if (mListener != null) {
                        mListener.onRefresh();
                    }


5. 前端界面設置回調(並非RefreshListView類,是使用了該listView類中寫!!!)

    lvList.setOnRefreshListener(new OnRefreshListener() {

            @Override
            public void onRefresh() {
                // 刷新數據
                getDataFromServer();
            }
        });


7. 刷新結束,收起控件

最後一步,刷新完成,恢復控件原始位置,狀態恢復,標題恢復,刷新完成的條件下更新時間。

public void onRefreshComplete(boolean success) {

        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

        mCurrentState = STATE_PULL_TO_REFRESH;
        tvTitle.setText("下拉刷新");
        pbProgress.setVisibility(View.INVISIBLE);
        ivArrow.setVisibility(View.VISIBLE);

        if (success) {// 只有刷新成功之後才更新時間
            setCurrentTime();
        }
    }




以上就是所有的邏輯,有關於”下拉刷新“的重點,其實各個細節都會有耦合的部分,盡量抽取出一個個小模塊進行講解,其中最重要的就是 第三點.滑動事件處理第六點.下拉刷新監聽回調。大致這幾個步驟,講的還是蠻細的,適合入門吧,也不知道這樣分開講好不好,總之,希望對你們有幫助 :)

(全部代碼等上拉加載寫完後 貼出來:)

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