Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義組合控件---教你如何自定義下拉刷新和左滑刪除

Android自定義組合控件---教你如何自定義下拉刷新和左滑刪除

編輯:關於Android編程

緒論

最近項目裡面用到了下拉刷新和左滑刪除,網上找了找並沒有可以用的,有比較好的左滑刪除,但是並沒有和下拉刷新上拉加載結合到一起,要不就是一些比較水的結合,並不能在項目裡面使用,小編一著急自己組合了一個,做完了和QQ的對比了一下,並沒有太大區別,今天分享給大家,其實並不難,但是不知道為什麼網上沒有比較好的Demo,當你的項目真的很急的時候,又沒有比較好的Demo,那麼“那條友誼的小船兒真是說翻就翻啊”,好了,下面先來具體看一下實現後的效果吧:

代碼已經上傳到Github上了,小伙伴們記得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo

這裡寫圖片描述
還不錯吧?比QQ多了個上拉加載,好了看看怎麼實現的吧,小編在之前的游客評論中了解到,代碼注釋很重要,所以盡量把注釋寫的很清楚:

實現思路

由於時間有限,左滑菜單是在網上找的Demo

自定義下拉刷新頭、尾 手勢判斷,根據滑動距離顯示頭部下拉布局 判斷是否滑動到底部顯示尾部上拉布局 創建左滑菜單,根據手勢滑動事件彈出菜單

詳細的看一下實現過程
1.首先我們先自定義下拉頭布局:




    

        

            

            

                

                
            
        

        

        
    

2.接下來我們自定義HeaderView,代碼很詳細了,不多介紹了

package com.hankkin.library;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;



public class RefreshListHeader extends LinearLayout {
    //根布局
    private LinearLayout mContainer;
    //下拉箭頭圖片
    private ImageView mArrowImageView;
    //下拉進度條
    private ProgressBar mProgressBar;
    //下拉文本
    private TextView mHintTextView;
    //狀態值 0-正常 1-准備刷新  2-正在刷新
    private int mState = STATE_NORMAL;

    //抬起動畫
    private Animation mRotateUpAnim;
    //下拉動畫
    private Animation mRotateDownAnim;

    //下拉動畫時間
    private final int ROTATE_ANIM_DURATION = 180;

    public final static int STATE_NORMAL = 0;
    public final static int STATE_READY = 1;
    public final static int STATE_REFRESHING = 2;

    public RefreshListHeader(Context context) {
        super(context);
        initView(context);
    }

    /**
     * @param context
     * @param attrs
     */
    public RefreshListHeader(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    /**
     * 初始化組件
     * @param context
     */
    private void initView(Context context) {
        LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, 0);
        mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_header, null);
        addView(mContainer, lp);
        setGravity(Gravity.BOTTOM);

        mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
        mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
        mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);
        //設置抬起動畫
        mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
        mRotateUpAnim.setFillAfter(true);
        //設置下拉動畫
        mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
        mRotateDownAnim.setFillAfter(true);
    }
    //設置狀態
    public void setState(int state) {
        if (state == mState)
            return;
        //正在刷新時-顯示進度條,隱藏下拉箭頭
        if (state == STATE_REFRESHING) {
            mArrowImageView.clearAnimation();
            mArrowImageView.setVisibility(View.INVISIBLE);
            mProgressBar.setVisibility(View.VISIBLE);
        } else {
            //顯示下拉箭頭,隱藏進度條
            mArrowImageView.setVisibility(View.VISIBLE);
            mProgressBar.setVisibility(View.INVISIBLE);
        }

        switch (state) {
        case STATE_NORMAL:
            if (mState == STATE_READY) {//准備刷新時開啟動畫
                mArrowImageView.startAnimation(mRotateDownAnim);
            }
            if (mState == STATE_REFRESHING) {//正在刷新時開啟動畫
                mArrowImageView.clearAnimation();
            }
            mHintTextView.setText(R.string.xlistview_header_hint_normal);
            break;
        case STATE_READY:
            if (mState != STATE_READY) {
                mArrowImageView.clearAnimation();
                mArrowImageView.startAnimation(mRotateUpAnim);
                mHintTextView.setText(R.string.xlistview_header_hint_ready);
            }
            break;
        case STATE_REFRESHING:
            mHintTextView.setText(R.string.xlistview_header_hint_loading);
            break;
        default:
        }

        mState = state;
    }

    /**
     * 設置頭部高度
     * @param height
     */
    public void setVisiableHeight(int height) {
        if (height < 0)
            height = 0;
        LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
        lp.height = height;
        mContainer.setLayoutParams(lp);
    }

    public int getVisiableHeight() {
        return mContainer.getHeight();
    }

}

3.HeaderView定義結束後,我們需要自定義一個既支持下拉刷新又支持左滑刪除的ListView,看看我是怎麼做的:(左滑菜單是引用網上的Demo,代碼寫的也比較易懂,這裡不詳細給大家介紹了)
然後我們在他的基礎上添加下拉上拉事件:(重點看一下onTouchEvent事件)

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) { //獲取上次y軸坐標
            mLastY = ev.getRawY();
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:   //手勢按下事件、獲取坐標、設置上次下拉時間
                firstTouchY = ev.getRawY();
                mLastY = ev.getRawY();
                setRefreshTime(RefreshTime.getRefreshTime(getContext()));
                int oldPos = mTouchPosition;
                mDownX = ev.getX();
                mDownY = ev.getY();
                mTouchState = TOUCH_STATE_NONE;

                mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());

                //彈出左滑菜單
                if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
                    mTouchState = TOUCH_STATE_X;
                    mTouchView.onSwipe(ev);//左滑菜單手勢監聽事件,根據滑動距離彈出菜單
                    return true;
                }


                //獲取item view,此方法是因為getChildAt()傳入index值導致listview不可見的item會報空指針
                // 防止listview不可見的item獲取到的為空,使用下面方法
                View view = getChildAt(mTouchPosition - getFirstVisiblePosition());

                if (mTouchView != null && mTouchView.isOpen()) {//如果滑動的item不為空並且已經開啟,則關閉該菜單
                    mTouchView.smoothCloseMenu();
                    mTouchView = null;
                    return super.onTouchEvent(ev);
                }

                if (mTouchView != null) {//否則打開左滑菜單
                    mTouchView.onSwipe(ev);
                }
                if (view instanceof SwipeMenuLayout) {
                    mTouchView = (SwipeMenuLayout) view;
                }

                break;
            case MotionEvent.ACTION_MOVE://手勢滑動事件
                final float deltaY = ev.getRawY() - mLastY;
                float dy = Math.abs((ev.getY() - mDownY));
                float dx = Math.abs((ev.getX() - mDownX));
                mLastY = ev.getRawY();
                //判斷左滑菜單是否未激活、或者x軸偏移平方小於y軸偏移平方3倍的時候
                if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {
                    //判斷第一個可見位置並且頭部布局可見高度大於0時或者y軸偏移量>0
                    if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
                        // 重新更新頭部高度
                        updateHeaderHeight(deltaY / OFFSET_RADIO);
                        invokeOnScrolling();
                    }
                }

                if (mTouchState == TOUCH_STATE_X) {//如果x軸偏移彈出左滑菜單
                    if (mTouchView != null) {
                        mTouchView.onSwipe(ev);
                    }
                    getSelector().setState(new int[] { 0 });
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                } else if (mTouchState == TOUCH_STATE_NONE) {
                    if (Math.abs(dy) > MAX_Y) { //如果y軸偏移量>指定y軸偏移量,設置y軸偏移狀態
                        mTouchState = TOUCH_STATE_Y;
                    } else if (dx > MAX_X) {//如果x軸偏移量>指定x軸偏移量,設置x軸偏移狀態,開始彈出左滑菜單
                        mTouchState = TOUCH_STATE_X;
                        if (mOnSwipeListener != null) {
                            mOnSwipeListener.onSwipeStart(mTouchPosition);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP://手勢抬起事件
                mLastY = -1; // reset
                if (getFirstVisiblePosition() == 0) {
                    // 設置下拉刷新狀態值,開啟下拉刷新狀態
                    if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                        mPullRefreshing = true;
                        mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);
                        if (onRefreshListener != null) {
                            tag=REFRESH;
                            onRefreshListener.onRefresh();
                        }
                    }
                    resetHeaderHeight();
                }

                lastTouchY = ev.getRawY();//獲取上次y軸偏移量
                if (canLoadMore()) {//判斷是否滿足上拉
                    loadData();
                }

                if (mTouchState == TOUCH_STATE_X) {//如果為x軸偏移狀態,開啟左滑
                    if (mTouchView != null) {
                        mTouchView.onSwipe(ev);
                        if (!mTouchView.isOpen()) {
                            mTouchPosition = -1;
                            mTouchView = null;
                        }
                    }
                    if (mOnSwipeListener != null) {
                        mOnSwipeListener.onSwipeEnd(mTouchPosition);
                    }
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

當然這部分也是滑動沖突解決的主要部分,看一下完整的代碼,應該能夠看的懂了,小編幾乎每一句都加上了注釋,如果有看不明白的地方可以留言,小編會及時回復的:

package com.hankkin.library;

import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

import java.util.Date;

public class RefreshSwipeMenuListView extends ListView implements OnScrollListener {

    private static final int TOUCH_STATE_NONE = 0;
    private static final int TOUCH_STATE_X = 1; //x軸觸摸狀態值
    private static final int TOUCH_STATE_Y = 2; //y軸觸摸狀態值
    public static final int BOTH=2;//上拉和下拉
    public static final int HEADER=0;//下拉
    public static final int FOOTER=1;//上拉
    public static String tag;//ListView的動作
    public static final String REFRESH="refresh";
    public static final String LOAD="load";
    private int MAX_Y = 5;  //Y軸最大偏移量
    private int MAX_X = 3;  //X軸最大偏移量
    private float mDownX;   //觸摸x
    private float mDownY;   //觸摸y
    private int mTouchState;    //觸摸狀態
    private int mTouchPosition; //觸摸位置
    private SwipeMenuLayout mTouchView; //滑動彈出布局
    private OnSwipeListener mOnSwipeListener;   //彈出監聽器

    private float firstTouchY;  //第一次觸摸y坐標
    private float lastTouchY;   //最後一次觸摸y坐標
    //創建左滑菜單接口
    private SwipeMenuCreator mMenuCreator;
    //菜單點擊事件
    private OnMenuItemClickListener mOnMenuItemClickListener;
    //關閉菜單動畫修飾Interpolator
    private Interpolator mCloseInterpolator;
    //開啟菜單動畫修飾Interpolator
    private Interpolator mOpenInterpolator;

    private float mLastY = -1;
    private Scroller mScroller;
    private OnScrollListener mScrollListener; // 滑動監聽

    // 下拉上拉監聽器
    private OnRefreshListener onRefreshListener;

    //下拉頭
    private RefreshListHeader mHeaderView;

    //頭部視圖內容,用來計算頭部高度,不下拉時隱藏
    private RelativeLayout mHeaderViewContent;
    //下拉時間文本控件
    private TextView mHeaderTimeView;
    private int mHeaderViewHeight; // 頭部高度
    private boolean mEnablePullRefresh = true;//能否下拉刷新
    private boolean mPullRefreshing = false; // 是否正在刷新

    //上拉尾部視圖
    private LinearLayout mFooterView;
    private boolean mEnablePullLoad;//是否可以上拉加載
    private boolean mPullLoading;   //是否正在上拉
    private boolean mIsFooterReady = false;
    private int mTotalItemCount;
    private int mScrollBack;
    private final static int SCROLLBACK_HEADER = 0;
    private final static int SCROLLBACK_FOOTER = 1;

    private final static int SCROLL_DURATION = 400;
    private final static int PULL_LOAD_MORE_DELTA = 50;
    private final static float OFFSET_RADIO = 1.8f;
    private boolean isFooterVisible=false;

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

    public RefreshSwipeMenuListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

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

    /**
     * 初始化組件
     * @param context
     */
    private void init(Context context) {
        mScroller = new Scroller(context, new DecelerateInterpolator());
        super.setOnScrollListener(this);
        // 初始化頭部視圖
        mHeaderView = new RefreshListHeader(context);
        mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);
        mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);
        addHeaderView(mHeaderView);

        // 初始化尾部視圖
        mFooterView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.xlistview_footer, null, false);

        // 初始化頭部高度
        mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mHeaderViewHeight = mHeaderViewContent.getHeight();
                //向 ViewTreeObserver 注冊方法,以獲取控件尺寸
                getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }
        });
        MAX_X = dp2px(MAX_X);
        MAX_Y = dp2px(MAX_Y);
        mTouchState = TOUCH_STATE_NONE;
    }

    /**
     * 添加適配器
     * @param adapter
     */
    @Override
    public void setAdapter(ListAdapter adapter) {
        if (mIsFooterReady == false) {  //添加尾部隱藏
            mIsFooterReady = true;
            addFooterView(mFooterView);
            mFooterView.setVisibility(GONE);
        }
        super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) {
            @Override
            public void createMenu(SwipeMenu menu) {//創建左滑菜單
                if (mMenuCreator != null) {
                    mMenuCreator.create(menu);
                }
            }

            @Override
            public void onItemClick(SwipeMenuView view, SwipeMenu menu, int index) {
                if (mOnMenuItemClickListener != null) {//左滑菜單點擊事件
                    mOnMenuItemClickListener.onMenuItemClick(view.getPosition(), menu, index);
                }
                if (mTouchView != null) {
                    mTouchView.smoothCloseMenu();
                }
            }
        });
    }

    public void setCloseInterpolator(Interpolator interpolator) {
        mCloseInterpolator = interpolator;
    }

    public void setOpenInterpolator(Interpolator interpolator) {
        mOpenInterpolator = interpolator;
    }

    public Interpolator getOpenInterpolator() {
        return mOpenInterpolator;
    }

    public Interpolator getCloseInterpolator() {
        return mCloseInterpolator;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) { //獲取上次y軸坐標
            mLastY = ev.getRawY();
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:   //手勢按下事件、獲取坐標、設置上次下拉時間
                firstTouchY = ev.getRawY();
                mLastY = ev.getRawY();
                setRefreshTime(RefreshTime.getRefreshTime(getContext()));
                int oldPos = mTouchPosition;
                mDownX = ev.getX();
                mDownY = ev.getY();
                mTouchState = TOUCH_STATE_NONE;

                mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());

                //彈出左滑菜單
                if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) {
                    mTouchState = TOUCH_STATE_X;
                    mTouchView.onSwipe(ev);//左滑菜單手勢監聽事件,根據滑動距離彈出菜單
                    return true;
                }


                //獲取item view,此方法是因為getChildAt()傳入index值導致listview不可見的item會報空指針
                // 防止listview不可見的item獲取到的為空,使用下面方法
                View view = getChildAt(mTouchPosition - getFirstVisiblePosition());

                if (mTouchView != null && mTouchView.isOpen()) {//如果滑動的item不為空並且已經開啟,則關閉該菜單
                    mTouchView.smoothCloseMenu();
                    mTouchView = null;
                    return super.onTouchEvent(ev);
                }

                if (mTouchView != null) {//否則打開左滑菜單
                    mTouchView.onSwipe(ev);
                }
                if (view instanceof SwipeMenuLayout) {
                    mTouchView = (SwipeMenuLayout) view;
                }

                break;
            case MotionEvent.ACTION_MOVE://手勢滑動事件
                final float deltaY = ev.getRawY() - mLastY;
                float dy = Math.abs((ev.getY() - mDownY));
                float dx = Math.abs((ev.getX() - mDownX));
                mLastY = ev.getRawY();
                //判斷左滑菜單是否未激活、或者x軸偏移平方小於y軸偏移平方3倍的時候
                if ((mTouchView == null || !mTouchView.isActive()) && Math.pow(dx, 2) / Math.pow(dy, 2) <= 3) {
                    //判斷第一個可見位置並且頭部布局可見高度大於0時或者y軸偏移量>0
                    if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
                        // 重新更新頭部高度
                        updateHeaderHeight(deltaY / OFFSET_RADIO);
                        invokeOnScrolling();
                    }
                }

                if (mTouchState == TOUCH_STATE_X) {//如果x軸偏移彈出左滑菜單
                    if (mTouchView != null) {
                        mTouchView.onSwipe(ev);
                    }
                    getSelector().setState(new int[] { 0 });
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                } else if (mTouchState == TOUCH_STATE_NONE) {
                    if (Math.abs(dy) > MAX_Y) { //如果y軸偏移量>指定y軸偏移量,設置y軸偏移狀態
                        mTouchState = TOUCH_STATE_Y;
                    } else if (dx > MAX_X) {//如果x軸偏移量>指定x軸偏移量,設置x軸偏移狀態,開始彈出左滑菜單
                        mTouchState = TOUCH_STATE_X;
                        if (mOnSwipeListener != null) {
                            mOnSwipeListener.onSwipeStart(mTouchPosition);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP://手勢抬起事件
                mLastY = -1; // reset
                if (getFirstVisiblePosition() == 0) {
                    // 設置下拉刷新狀態值,開啟下拉刷新狀態
                    if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                        mPullRefreshing = true;
                        mHeaderView.setState(RefreshListHeader.STATE_REFRESHING);
                        if (onRefreshListener != null) {
                            tag=REFRESH;
                            onRefreshListener.onRefresh();
                        }
                    }
                    resetHeaderHeight();
                }

                lastTouchY = ev.getRawY();//獲取上次y軸偏移量
                if (canLoadMore()) {//判斷是否滿足上拉
                    loadData();
                }

                if (mTouchState == TOUCH_STATE_X) {//如果為x軸偏移狀態,開啟左滑
                    if (mTouchView != null) {
                        mTouchView.onSwipe(ev);
                        if (!mTouchView.isOpen()) {
                            mTouchPosition = -1;
                            mTouchView = null;
                        }
                    }
                    if (mOnSwipeListener != null) {
                        mOnSwipeListener.onSwipeEnd(mTouchPosition);
                    }
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    class ResetHeaderHeightTask extends AsyncTask {
        protected Void doInBackground(Void... params) {
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        protected void onPostExecute(Void result) {
            mPullRefreshing = false;
            mHeaderView.setState(RefreshListHeader.STATE_NORMAL);
            resetHeaderHeight();

        }
    }

    public void smoothOpenMenu(int position) {
        if (position >= getFirstVisiblePosition() && position <= getLastVisiblePosition()) {
            View view = getChildAt(position - getFirstVisiblePosition());
            if (view instanceof SwipeMenuLayout) {
                mTouchPosition = position;
                if (mTouchView != null && mTouchView.isOpen()) {
                    mTouchView.smoothCloseMenu();
                }
                mTouchView = (SwipeMenuLayout) view;
                mTouchView.smoothOpenMenu();
            }
        }
    }

    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                getContext().getResources().getDisplayMetrics());
    }

    public void setMenuCreator(SwipeMenuCreator menuCreator) {
        this.mMenuCreator = menuCreator;
    }

    public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
        this.mOnMenuItemClickListener = onMenuItemClickListener;
    }

    public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
        this.mOnSwipeListener = onSwipeListener;
    }

    public static interface OnMenuItemClickListener {
        void onMenuItemClick(int position, SwipeMenu menu, int index);
    }

    public static interface OnSwipeListener {
        void onSwipeStart(int position);

        void onSwipeEnd(int position);
    }

    /**
     * 設置刷新可用
     * @param enable
     */
    private void setPullRefreshEnable(boolean enable) {
        mEnablePullRefresh = enable;
        if (!mEnablePullRefresh) { // disable, hide the content
            mHeaderViewContent.setVisibility(View.INVISIBLE);
        } else {
            mHeaderViewContent.setVisibility(View.VISIBLE);
        }
    }

    /**
     * enable or disable pull up load more feature.
     * 設置加載可用
     * @param enable
     */
    private void setPullLoadEnable(boolean enable) {
        mEnablePullLoad = enable;
        if (!mEnablePullLoad) {
            mFooterView.setVisibility(GONE);
            mFooterView.setOnClickListener(null);
        } else {
            mPullLoading = false;
            mFooterView.setVisibility(VISIBLE);
            // both "pull up" and "click" will invoke load more.
            mFooterView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    startLoadMore();
                }
            });
        }
    }

    /**
     * stop refresh, reset header view.
     * 停止刷新,重置頭部控件
     */
    private void stopRefresh() {
        if (mPullRefreshing == true) {
            mPullRefreshing = false;
            resetHeaderHeight();
        }
    }

    /**
     * stop load more, reset footer view.
     * 停止加載更多,重置尾部控件
     */
    private void stopLoadMore() {
        if (mPullLoading == true) {
            mPullLoading = false;
            mFooterView.setVisibility(GONE);
        }
    }

    /**
     * set last refresh time
     *
     * @param time
     */
    public void setRefreshTime(String time) {
        mHeaderTimeView.setText(time);
    }

    private void invokeOnScrolling() {
        if (mScrollListener instanceof OnXScrollListener) {
            OnXScrollListener l = (OnXScrollListener) mScrollListener;
            l.onXScrolling(this);
        }
    }

    /**
     * 更新頭部高度,設置狀態值
     * @param delta
     */
    private void updateHeaderHeight(float delta) {
        mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());
        if (mEnablePullRefresh && !mPullRefreshing) {
            if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                mHeaderView.setState(RefreshListHeader.STATE_READY);
            } else {
                mHeaderView.setState(RefreshListHeader.STATE_NORMAL);
            }
        }
        setSelection(0); // scroll to top each time
    }

    /**
     * 重置頭部視圖高度
     */
    private void resetHeaderHeight() {
        int height = mHeaderView.getVisiableHeight();
        if (height == 0) // 不可見
            return;
        // 如果正在刷新並且頭部高度沒有完全顯示不做操作
        if (mPullRefreshing && height <= mHeaderViewHeight) {
            return;
        }
        int finalHeight = 0; // 默認
        //如果正在刷新並且滑動高度大於頭部高度
        if (mPullRefreshing && height > mHeaderViewHeight) {
            finalHeight = mHeaderViewHeight;
        }
        mScrollBack = SCROLLBACK_HEADER;
        mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);
        // 觸發computescroll
        invalidate();
    }


    /**
     * 開啟上啦
     */
    private void startLoadMore() {
        mPullLoading = true;
        mFooterView.setVisibility(VISIBLE);
        if (onRefreshListener != null) {
            tag=LOAD;
            onRefreshListener.onLoadMore();
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            if (mScrollBack == SCROLLBACK_HEADER) {
                mHeaderView.setVisiableHeight(mScroller.getCurrY());
            } 
            postInvalidate();
            invokeOnScrolling();
        }
        super.computeScroll();
    }

    @Override
    public void setOnScrollListener(OnScrollListener l) {
        mScrollListener = l;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mScrollListener != null) {
            mScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    /**
     * 滑動監聽
     * @param view
     * @param firstVisibleItem
     * @param visibleItemCount
     * @param totalItemCount
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mTotalItemCount = totalItemCount;
        if (mScrollListener != null) {
            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
        if(firstVisibleItem+visibleItemCount>=totalItemCount){
            isFooterVisible=true;
        }else{
            isFooterVisible=false;
        }
    }

    public void setOnRefreshListener(OnRefreshListener l) {
        onRefreshListener = l;
    }

    /**
     * you can listen ListView.OnScrollListener or this one. it will invoke
     * onXScrolling when header/footer scroll back.
     */
    public interface OnXScrollListener extends OnScrollListener {
        public void onXScrolling(View view);
    }

    /**
     * implements this interface to get refresh/load more event.
     */
    public interface OnRefreshListener {
        public void onRefresh();
        public void onLoadMore();
    }

    /**
     * 上拉加載和下拉刷新請求完畢
     */
    public void complete(){
        stopLoadMore();
        stopRefresh();
        if(REFRESH.equals(tag)){
            RefreshTime.setRefreshTime(getContext(),new Date());
        }
    }

    /**
     * 設置ListView的模式,上拉和下拉
     * @param mode
     */
    public void setListViewMode(int mode){
        if(mode==BOTH){
            setPullRefreshEnable(true);
            setPullLoadEnable(true);
        }else if(mode==FOOTER){
            setPullLoadEnable(true);
        }else if(mode==HEADER){
            setPullRefreshEnable(true);
        }
    }

    /**
     * 判斷是否可以上蠟加載
     * @return
     */
    private boolean canLoadMore() {
        return isBottom() && !mPullLoading && isPullingUp();
    }

    /**
     * 判斷是否到達底部
     * @return
     */
    private boolean isBottom() {
        if (getCount() > 0) {
            if (getLastVisiblePosition() == getAdapter().getCount() - 1 &&
                    getChildAt(getChildCount() - 1).getBottom() <= getHeight()) {
                return true;
            }
        }
        return false;
    }

    private boolean isPullingUp() {
        return (firstTouchY - lastTouchY) >= 200;
    }


    private void loadData() {
        if (onRefreshListener != null) {
            setLoading(true);
        }
    }

    /**
     * 設置是否上拉
     * @param loading
     */
    public void setLoading(boolean loading) {
        if (this == null) return;
        mPullLoading = loading;
        if (loading) {
            mFooterView.setVisibility(VISIBLE);

            setSelection(getAdapter().getCount() - 1);
            onRefreshListener.onLoadMore();
        } else {
            mFooterView.setVisibility(GONE);
            firstTouchY = 0;
            lastTouchY = 0;
        }
    }

}

定義完了之後我們是這樣用的:

rsmLv.setAdapter(adapter);
        rsmLv.setListViewMode(RefreshSwipeMenuListView.HEADER);
        rsmLv.setOnRefreshListener(this);

        SwipeMenuCreator creator = new SwipeMenuCreator() {
            @Override
            public void create(SwipeMenu menu) {
                // 創建滑動選項
                SwipeMenuItem rejectItem = new SwipeMenuItem(
                        getApplicationContext());
                // 設置選項背景
                rejectItem.setBackground(new ColorDrawable(getResources().getColor(R.color.top)));
                // 設置選項寬度
                rejectItem.setWidth(dp2px(80,getApplicationContext()));
                // 設置選項標題
                rejectItem.setTitle("置頂");
                // 設置選項標題
                rejectItem.setTitleSize(16);
                // 設置選項標題顏色
                rejectItem.setTitleColor(Color.WHITE);
                // 添加選項
                menu.addMenuItem(rejectItem);

                // 創建刪除選項
                SwipeMenuItem argeeItem = new SwipeMenuItem(getApplicationContext());
                argeeItem.setBackground(new ColorDrawable(getResources().getColor(R.color.del)));
                argeeItem.setWidth(dp2px(80, getApplicationContext()));
                argeeItem.setTitle("刪除");
                argeeItem.setTitleSize(16);
                argeeItem.setTitleColor(Color.WHITE);
                menu.addMenuItem(argeeItem);
            }
        };

        rsmLv.setMenuCreator(creator);

        rsmLv.setOnMenuItemClickListener(new RefreshSwipeMenuListView.OnMenuItemClickListener() {
            @Override
            public void onMenuItemClick(int position, SwipeMenu menu, int index) {
                switch (index) {
                    case 0: //第一個選項
                        Toast.makeText(MainActivity.this,"您點擊的是置頂",Toast.LENGTH_SHORT).show();
                        break;
                    case 1: //第二個選項
                        del(position,rsmLv.getChildAt(position+1-rsmLv.getFirstVisiblePosition()));
                        break;

                }
            }
        });

為了和QQ有著一樣的體驗效果,小編加了一個刪除item的動畫
看起來就不會太生硬了,用戶體驗也比較好:

/**
     * 刪除item動畫
     * @param index
     * @param v
     */
    private void del(final int index, View v){
        final Animation animation = (Animation) AnimationUtils.loadAnimation(v.getContext(), R.anim.list_anim);
        animation.setAnimationListener(new Animation.AnimationListener() {
            public void onAnimationStart(Animation animation) {}
            public void onAnimationRepeat(Animation animation) {}
            public void onAnimationEnd(Animation animation) {
                data.remove(index);
                adapter.notifyDataSetChanged();
                animation.cancel();
            }
        });

        v.startAnimation(animation);
    }

好了到這裡了,我們的組合控件-上拉下拉+左滑刪除的組合ListView就定義好了,分享出來小伙伴們以後遇到可以直接拿來用,”到時候小船兒也不會再翻了”,不合理的地方希望大家提出來,共同交流,進步。

代碼已經上傳到Github上了,小伙伴們記得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo

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