Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義View——利用下拉刷新組件實現上拉加載

自定義View——利用下拉刷新組件實現上拉加載

編輯:關於Android編程

注:本文demo已經提交github,地址完整代碼如下,demo工程已經上傳至GitHub,
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout
感謝大家支持!

在Android開發中,我們經常會用到列表下拉刷新和上拉加載的功能。
Google在support.v4包中提供了一個組件可以用來進行下來刷新,這個組件是SwipeRefreshLayout。
下面我們來看一下這個組件的使用:
在布局文件中加上xml代碼



        

在MainActivity.java代碼中添加代碼:

/**
 * 添加變量
 * /
private SwipeRefreshLayout swipeRefreshLayout;
private ListView listView;
private ArrayList list = new ArrayList<>();
private ArrayAdapter adapter;

在Activity的onCreate方法中初始化實例

/**
 * 實例化變量,此處省略initList()方法
 * 此方法內容為添加了N個字符串"item"
 */
initList();
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
listView = (ListView) findViewById(R.id.list_view);
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);

接下來給SwipeRefreshLayout添加OnRefreshListener監聽

swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        Toast.makeText(MainActivity.this, "is refreshing!",          Toast.LENGTH_SHORT).show();
    }
});

運行效果如圖所示
這裡寫圖片描述

但是在日常開發中,我們不僅需要下拉刷新的功能,還應該有上拉繼續加載的功能,但是Google並沒有給我們提供上拉加載的方法,那怎麼辦呢?我們可以自定義一個View,自己添加上拉加載方法。

我們模仿Swiperefreshlayout的使用下拉刷新的方法,去設計上拉加載的方法。添加了上拉加載的代碼形式大約為<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KCgoKPHByZSBjbGFzcz0="brush:java;">view.setOnLoadListener(new OnLoadListener() { @Override public void onLoad() { //在此執行上拉加載的邏輯 view.setloading(false); //邏輯執行完畢後停止上拉加載 } });

所以,我們在重寫下拉刷新組件前先寫一個接口OnLoadListener

public interface OnLoadListener {
    public void onLoad();  //需要用戶重寫的方法
}

我們寫一個UpLoadSwipeRefreshLayout類,繼承SwipeRefershLayout類。我們應該在什麼時候開始上拉加載呢?當然是在子ListView滑動到最底部的時候,所以,我們可以開始進行上拉加載的時候應該滿足一下條件:
① 判斷listview已經滑動到了最底部
② 判斷手指是在向上滑動
③ 當前上拉加載不正在進行

編寫類
代碼如下:

public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout {

   private ListView mListView;  //子view

    private boolean isCanLoading = false;  //是否正可以加載,默認不可用

    private OnLoadListener mOnLoadListener;

    private View mFooterView;  //上拉加載的視圖

    private int firstY;
    private int lastY;

    private int mTouchSlop;  //最短的滑動距離

    private int footerResource;  //加載視圖的資源ID

    public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
     mTouchSlop =    ViewConfiguration.get(getContext()).getScaledTouchSlop();  //獲取系統默認的最短滑動距離
    }
}

因為ListView是此組件的子view,那麼我們應該在開始的時候去檢測,子view是否是listview。

重寫onLayout()方法,獲取子類

@Override
    protected void onLayout(boolean changed, int left, int        top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (mListView == null) {
            getListView();  //獲取子view
        }
 }

構造一個getListView方法

private void getListView() {
        int childs = getChildCount();
        if (childs > 0) {
            View childView = getChildAt(0);  //獲取第一個子view
            if (childView instanceof ListView) {
                /**
                 * 如果子view的類型是ListView
                 */
                mListView = (ListView) childView;
                //給子listview設置滑動監聽
                mListView.setOnScrollListener(this);
            }
        }
    }

重寫事件攔截事件,防止滑動事件發生沖突

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                /**
                 * 獲得觸摸事件剛剛開始的時候,手指觸摸的坐標
                 */
                firstY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * 獲得觸摸事件結束的時候,手指離開時候的坐標
                 */
                lastY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                if (isCanLoad()) {
                    loadData();
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

為了獲取子listview的滑動情況,使用我們應該讓組件實現OnScrollListener接口。

class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout                  implements AbsListView.OnScrollListener {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {}

    /**
     * 重寫滑動時間
     * /
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                         int totalItemCount) {
        if (isCanLoad()) {
            /**
             * 滑動過程中如果可以加載數據
             * 就執行加載數據的函數
             */
            loadData();
        }
    }
}

編寫isCanLoad()方法,這裡就用到了上方我們需要滿足的三個條件

private boolean isCanLoad() {
    return isBottom() && isPushUp() && !isCanLoading;
}

依次編寫2個方法

/**
 * 判斷listview是否到了底部
 */
private boolean isBottom() {
    if (mListView != null && mListView.getAdapter() != null) {
        /**
         * 當前可加的最後一項就是listview的末尾項
         * 說明滑動到了最底部
         */
        return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
    }
    return false;
}
/**
 * 判斷是否正在上拉
 */
private boolean isPushUp() {
    /**
     * 往上滑動的距離大於系統默認的滑動距離
     */
    return firstY - lastY >= mTouchSlop;
}

這時候我們還需要編寫loadData()方法

/**
 * 加載數據
 */
private void loadData() {
    if (mOnLoadListener != null) {
        setLoading(true);
        /**
         * 處理加載的邏輯
         * 具體邏輯在回調中完成
         */
        mOnLoadListener.onLoad();
    }
}

編寫設置可加載的方法setLoading()

public void setLoading(boolean isCanLoad) {
    this.isCanLoading = isCanLoad;
    if (isCanLoad == true) {
        /**
        * 加載,添加底部的正在加載視圖
        */
        mListView.addFooterView(mFooterView);
    } else {
        /**
         * 不在加載中,將底部的加載中視圖移除
         */
        mListView.removeFooterView(mFooterView);
        firstY = 0;
        lastY = 0;
    }
}

對了,還沒添加設置上拉加載監聽的方法!
編寫setOnUpLoadListener()方法

public void setOnLoadListener(OnLoadListener onLoadListener) {
    this.mOnLoadListener = onLoadListener;
}

這個類我對外提供了一個變量,代表listview的底視圖的資源id
可以通過setFooterResource方法進行添加並實例化此底部view
編寫此方法

public void setFooterResource(int footerResource) {
    this.footerResource = footerResource;
    /**
     * 實例化布局view
     */
    this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false);
}

這時候這個類我們就編寫好了,完整的代碼我會在博客的結尾放出。
我們先來看看這段代碼的使用
將剛才的布局文件修改成如下:



        

    

添加尾視圖的資源id文件,layout_up_load_view.xml




    

        

        

    

修改MainActivity.java的代碼

swipeRefreshLayout.setFooterResource(R.layout.layout_up_load_view);  //添加正在加載中的視圖
swipeRefreshLayout.setOnLoadListener(new OnLoadListener() {
    @Override
    public void onLoad() {    
        //上拉加載的操作
    }
});

運行效果如圖:
這裡寫圖片描述

完整代碼如下,demo工程已經上傳至GitHub
github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout

package com.example.chenglei.myapplication;

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by chenglei on 2016/4/20.
 * 重寫下拉刷新組件,添加上拉加載的功能
 */
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {

    private ListView mListView;  //子view

    private boolean isCanLoading = false;  //是否正可以加載,默認不可用

    private OnLoadListener mOnLoadListener;

    private View mFooterView;  //上拉加載的視圖

    private int firstY;
    private int lastY;

    private int mTouchSlop;  //最短的滑動距離

    private int footerResource;

    public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  //獲取系統默認的最短滑動距離
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (mListView == null) {
            getListView();
        }
    }

    /**
     * 獲取子view
     */
    private void getListView() {
        int childs = getChildCount();
        if (childs > 0) {
            View childView = getChildAt(0);  //獲取第一個子view
            if (childView instanceof ListView) {
                /**
                 * 如果子view的類型是ListView
                 */
                mListView = (ListView) childView;
                //給子listview設置滑動監聽
                mListView.setOnScrollListener(this);
            }
        }
    }

    /**
     * 觸摸事件攔截
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                /**
                 * 獲得觸摸事件剛剛開始的時候,手指觸摸的坐標
                 */
                firstY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * 獲得觸摸事件結束的時候,手指離開時候的坐標
                 */
                lastY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                if (isCanLoad()) {
                    loadData();
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 判斷是否可以進行加載
     */
    private boolean isCanLoad() {
        return isBottom() && isPushUp() && !isCanLoading;
    }

    /**
     * 判斷listview是否到了底部
     */
    private boolean isBottom() {
        if (mListView != null && mListView.getAdapter() != null) {
            /**
             * 當前可加的最後一項就是listview的末尾項
             * 說明滑動到了最底部
             */
            return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
        }
        return false;
    }

    /**
     * 判斷是否正在上拉
     */
    private boolean isPushUp() {
        /**
         * 往上滑動的距離大於系統默認的滑動距離
         */
        return firstY - lastY >= mTouchSlop;
    }

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

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                         int totalItemCount) {
        if (isCanLoad()) {
            /**
             * 滑動過程中如果可以加載數據
             * 就執行加載數據的函數
             */
            loadData();
        }
    }

    /**
     * 加載數據
     */
    private void loadData() {
        if (mOnLoadListener != null) {
            setLoading(true);
            /**
             * 處理加載的邏輯
             * 具體邏輯在回調中完成
             */
            mOnLoadListener.onLoad();
        }
    }

    /**
     * 設置可加載
     */
    public void setLoading(boolean isCanLoad) {
        this.isCanLoading = isCanLoad;
        if (isCanLoad == true) {
            /**
             * 加載,添加底部的正在加載視圖
             */
            mListView.addFooterView(mFooterView);
        } else {
            /**
             * 不在加載中,將底部的加載中視圖移除
             */
            mListView.removeFooterView(mFooterView);
            firstY = 0;
            lastY = 0;
        }
    }

    /**
     * 設置上拉加載監聽
     */
    public void setOnLoadListener(OnLoadListener onLoadListener) {
        this.mOnLoadListener = onLoadListener;
    }

    /**
     * 設置上拉加載的布局
     */
    public void setFooterResource(int footerResource) {
        this.footerResource = footerResource;
        /**
         * 實例化布局view
         */
        this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false);
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved