Android自定义组合控件---教你如何自定义下拉刷新和左滑删除

Posted Hankkin_Coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义组合控件---教你如何自定义下拉刷新和左滑删除相关的知识,希望对你有一定的参考价值。

绪论

最近项目里面用到了下拉刷新和左滑删除,网上找了找并没有可以用的,有比较好的左滑删除,但是并没有和下拉刷新上拉加载结合到一起,要不就是一些比较水的结合,并不能在项目里面使用,小编一着急自己组合了一个,做完了和QQ的对比了一下,并没有太大区别,今天分享给大家,其实并不难,但是不知道为什么网上没有比较好的Demo,当你的项目真的很急的时候,又没有比较好的Demo,那么“那条友谊的小船儿真是说翻就翻啊”,好了,下面先来具体看一下实现后的效果吧:

代码已经上传到Github上了,小伙伴们记得star和follow哦
https://github.com/Hankkin/SwipeRefreshDemo


还不错吧?比QQ多了个上拉加载,好了看看怎么实现的吧,小编在之前的游客评论中了解到,代码注释很重要,所以尽量把注释写的很清楚:

实现思路

由于时间有限,左滑菜单是在网上找的Demo

  1. 自定义下拉刷新头、尾
  2. 手势判断,根据滑动距离显示头部下拉布局
  3. 判断是否滑动到底部显示尾部上拉布局
  4. 创建左滑菜单,根据手势滑动事件弹出菜单

详细的看一下实现过程
1.首先我们先自定义下拉头布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="bottom" >

    <RelativeLayout
        android:id="@+id/xlistview_header_content"
        android:layout_width="fill_parent"
        android:background="#00000000"
        android:layout_height="60dp" >

        <LinearLayout
            android:id="@+id/xlistview_header_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/xlistview_header_hint_textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#000"
                android:textSize="16sp"
                android:text="@string/xlistview_header_hint_normal" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="3dp" >

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#000"
                    android:text="@string/xlistview_header_last_time"
                    android:textSize="14sp" />

                <TextView
                    android:id="@+id/xlistview_header_time"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#000"
                    android:textSize="14sp" />
            </LinearLayout>
        </LinearLayout>

        <ImageView
            android:id="@+id/xlistview_header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-50dp"
            android:src="@drawable/xlistview_arrow" />

        <ProgressBar
            android:id="@+id/xlistview_header_progressbar"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_alignLeft="@id/xlistview_header_text"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-55dp"
            android:visibility="gone"/>
    </RelativeLayout>

</LinearLayout>

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<Void, Void, Void> 
        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<

以上是关于Android自定义组合控件---教你如何自定义下拉刷新和左滑删除的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义View 三板斧之一——组合现有控件

转android UI进阶之自定义组合控件

如何打造Android自定义的下拉列表框控件

一手遮天 Android

android关于自定义seekbar控件的问题(将横向seekbar改成竖向seekbar)

Android自定义View之自定义组合控件