Android 自定义下拉刷新列表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chennai1101/article/details/88055844

1. 自定义ListView

自定义CListView继承ListView,实现AbsListView.OnScrollListener接口。
CListView添加CListViewHeaderView作为表头,使用表头显示状态,在加入表头以后列表的数量是Adapter的数量加上1。
CListView捕捉手势操作,监听滚动事件并设置刷新事件。

public class CListView extends ListView implements AbsListView.OnScrollListener {
    private CListViewHeaderView mHeaderView;
    private int mFirstItemIndex;

    private float mStartY, mCurrentY;
    private boolean mRefreshEnable, mRecord;

    public CListView(Context context) {
        this(context, null);
    }

    public CListView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mHeaderView = new CListViewHeaderView(context);
        mHeaderView.setListView(this);
        addHeaderView(mHeaderView);
        mHeaderView.refreshFinish();

        setOnScrollListener(this);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstItemIndex = firstVisibleItem;
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 保证触摸的y值只记录一次
                if (!mRecord) {
                    mStartY = event.getY();
                    mRecord = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (!mRecord) {
                    mStartY = event.getY();
                    mRecord = true;
                }
                mCurrentY = event.getY();
                if (mRefreshEnable && mFirstItemIndex == 0) {
                    mHeaderView.move((int) (mCurrentY - mStartY));
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mCurrentY = event.getY();
                if (mRefreshEnable && mFirstItemIndex == 0 && mRecord) {
                    mHeaderView.release((int) (mCurrentY - mStartY));
                }
                mRecord = false;
                break;
        }
        return super.onTouchEvent(event);
    }

    /*
     * 刷新回调
     */
    public void refreshFinish() {
        mHeaderView.refreshFinish();
    }

    public void setOnRefreshListener(IOnRefreshListener refreshListener) {
        mRefreshEnable = (refreshListener != null);
        mHeaderView.setOnRefreshListener(refreshListener);
    }

    public interface IOnRefreshListener {
        void onRefreshStart();
    }

}

2. 列表头CListViewHeaderView

CListViewHeaderView拥有四种状态

  • DONE,初始状态
  • PULL_TO_REFRESH,下拉刷新
  • RELEASE_TO_REFRESH,松开刷新
  • REFRESHING,刷新中

CListViewHeaderView默认是初始状态,当进行下拉操作时,显示为下拉刷新。下拉超过3倍表头高度,显示松开刷新。正在刷新时不能进行界面操作。

public class CListViewHeaderView extends LinearLayout {
    private final static int DONE               = 0;
    private final static int PULL_TO_REFRESH    = 1; // 下拉刷新
    private final static int RELEASE_TO_REFRESH = 2; // 放手刷新
    private final static int REFRESHING         = 3; // 刷新

    private final static int RATIO              = 3;

    private ImageView mIvArrow;
    private ProgressBar mProgressBarRefresh;
    private TextView mTvRefreshTips;

    private Animation mAnimCycle, mAnimReverse;

    private String mPullToRefresh, mReleaseToRefresh, mRefreshing;
    private int mContentHeight;

    private int mHeadState = DONE;
    private boolean mReverse; // 图标是否旋转

    private CListView mListView;
    private CListView.IOnRefreshListener mListener;

    public CListViewHeaderView(Context context) {
        this(context, null);
    }

    public CListViewHeaderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        inflate(context, R.layout.list_view_header_view, this);

        mIvArrow = findViewById(R.id.iv_arrow);
        mProgressBarRefresh = findViewById(R.id.progress_bar_refresh);
        mTvRefreshTips = findViewById(R.id.tv_refresh_tips);

        mAnimCycle = AnimationUtils.loadAnimation(context, R.anim.anim_list_view_rotate);
        mAnimReverse = AnimationUtils.loadAnimation(context, R.anim.anim_list_view_reverse);

        mContentHeight = context.getResources().getDimensionPixelOffset(R.dimen.head_view_height);

        mPullToRefresh = context.getString(R.string.list_view_pull_to_refresh);
        mReleaseToRefresh = context.getString(R.string.list_view_release_to_refresh);
        mRefreshing = context.getString(R.string.list_view_refreshing);
    }

    void setListView(CListView listView) {
        this.mListView = listView;
    }

    void move(int distance) {
        if (mHeadState != REFRESHING) {
            if (mHeadState == DONE) {
                if (distance > 0) {
                    mHeadState = PULL_TO_REFRESH;
                    changeHeaderViewByState();
                }
            }
            if (mHeadState == PULL_TO_REFRESH) {
                if (distance >= RATIO * mContentHeight) {
                    mHeadState = RELEASE_TO_REFRESH;
                    mReverse = true;
                    changeHeaderViewByState();
                } else if (distance <= 0) {
                    mHeadState = DONE;
                    changeHeaderViewByState();
                }
            }
            if (mHeadState == RELEASE_TO_REFRESH) {
                if (distance <= 0) {
                    mHeadState = DONE;
                    changeHeaderViewByState();
                } else if (distance < RATIO * mContentHeight) {
                    mHeadState = PULL_TO_REFRESH;
                    changeHeaderViewByState();
                }
            }
            if (mHeadState == PULL_TO_REFRESH || mHeadState == RELEASE_TO_REFRESH) {
                if (mListView != null) {
                    mListView.setSelection(0);
                }
                setPadding(0, distance / RATIO - mContentHeight, 0, 0);
            }
        }
    }

    void release(float distance) {
        if (distance >= RATIO * mContentHeight) {
            mHeadState = REFRESHING;
        } else {
            mHeadState = DONE;
        }
        changeHeaderViewByState();

        if (mHeadState == REFRESHING) {
            mListener.onRefreshStart();
        }
    }

    void setOnRefreshListener(CListView.IOnRefreshListener listener) {
        this.mListener = listener;
    }

    public void refreshFinish() {
        mHeadState = DONE;
        changeHeaderViewByState();
    }

    private void changeHeaderViewByState() {
        switch (mHeadState) {
            case DONE:
                setPadding(0, -1 * mContentHeight, 0, 0);

                mIvArrow.clearAnimation();
                mProgressBarRefresh.setVisibility(View.GONE);
                mTvRefreshTips.setText(mPullToRefresh);
                break;
            case PULL_TO_REFRESH:
                mIvArrow.setVisibility(View.VISIBLE);
                mIvArrow.clearAnimation();

                mProgressBarRefresh.setVisibility(View.GONE);
                mTvRefreshTips.setText(mPullToRefresh);

                // 是由RELEASE_To_REFRESH状态转变来的
                if (mReverse) {
                    mReverse = false;
                    mIvArrow.startAnimation(mAnimReverse);
                }
                break;
            case RELEASE_TO_REFRESH:
                mIvArrow.setVisibility(View.VISIBLE);
                mIvArrow.clearAnimation();

                mIvArrow.startAnimation(mAnimCycle);

                mProgressBarRefresh.setVisibility(View.GONE);
                mTvRefreshTips.setText(mReleaseToRefresh);
                break;
            case REFRESHING:
                setPadding(0, 0, 0, 0);

                mIvArrow.clearAnimation();
                mIvArrow.setVisibility(View.GONE);

                mProgressBarRefresh.setVisibility(View.VISIBLE);
                mTvRefreshTips.setText(mRefreshing);
                break;
        }
    }

}

资源文件list_view_header_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/header_view"
        android:layout_width="match_parent"
        android:layout_height="@dimen/head_view_height"
        android:orientation="horizontal"
        android:gravity="center">
        <ImageView
            android:id="@+id/iv_arrow"
            android:layout_width="12dp"
            android:layout_height="12dp"
            android:src="@drawable/list_arrow"
            android:scaleType="fitCenter"
            android:visibility="gone"/>

        <ProgressBar
            android:id="@+id/progress_bar_refresh"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv_refresh_tips"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:textSize="12sp"
            android:textColor="#FF000000"/>
    </LinearLayout>
</LinearLayout>

3. 界面调用

Activity中添加CListView.IOnRefreshListener监听器,并在3秒后结束。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ...  ...

    final CListView listView = findViewById(R.id.list_view);
    listView.setAdapter(new ArrayAdapter<>(this,
            R.layout.list_view_item, R.id.tv_name, getResources().getStringArray(R.array.month)));

    listView.setOnRefreshListener(new CListView.IOnRefreshListener() {
        @Override
        public void onRefreshStart() {
            listView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    listView.refreshFinish();
                }
            }, 3000);
        }
    });
}

4. 效果如下

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/chennai1101/article/details/88055844