26
2017
09

Android自定义控件之下拉刷新加载更多

继承ListView
- 给ListView 加个Header头部, 用来下拉刷新
1. 静态的头部加上去
2. 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
3. 手指抬起时, 根据当前状态,更新头布局

    - 给ListView 加个Footer脚部, 用来加载更多

这里写图片描述

这里写图片描述

public class RefreshListView extends ListView implements OnScrollListener{

    private View mHeaderView; // 头布局
    private int downY; // 按下时 Y 轴坐标值
    private int mHeaderViewHeight;

    private final int PULL_DOWN = 0; // 下拉刷新状态
    private final int RELEASE_REFRESH = 1; // 释放刷新状态
    private final int REFRESHING = 2; // 正在刷新中...

    int currentState = PULL_DOWN;// 默认是下拉刷新状态
    private ProgressBar pbHeaderLoading;
    private TextView tvHeaderState;
    private TextView tvHeaderLastRefreshTime;
    private ImageView ivHeaderArrow;
    private RotateAnimation rotateUp;
    private RotateAnimation rotateDown;

    private OnRefreshListener listener; // 监听回调
    private View mFooterView;  // 脚布局
    private int mFooterViewHeight; //脚布局高度
    private boolean isLoadMore = false; // 是否是加载更多

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

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

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

    /** * 初始化 * onMeasure -> onLayout -> onDraw * */
    private void init() {
        initHeaderView();
        initAnimation();

        initFooterView();

        setOnScrollListener(this);
    }

    /** * 初始化脚布局 */
    private void initFooterView() {
        mFooterView = View.inflate(getContext(), R.layout.layout_list_footer, null);

        // 隐藏脚布局
        mFooterView.measure(0, 0);
        mFooterViewHeight = mFooterView.getMeasuredHeight();

        mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);

        this.addFooterView(mFooterView);
    }

    private void initAnimation() {
        // 箭头向上的动画
        rotateUp = new RotateAnimation(
                0, -180, 
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateUp.setDuration(500);
        rotateUp.setFillAfter(true);

        // 箭头向下的动画
        rotateDown = new RotateAnimation(
                -180, -360,
                Animation.RELATIVE_TO_SELF, 0.5f, 
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateDown.setDuration(500);
        rotateDown.setFillAfter(true);

    }

    /** * 初始化Header, 头布局 */
    private void initHeaderView() {
        mHeaderView = View.inflate(getContext(), R.layout.layout_list_header, null);
        pbHeaderLoading = (ProgressBar) mHeaderView.findViewById(R.id.pb_header_loading);
        pbHeaderLoading.setVisibility(View.INVISIBLE);

        tvHeaderState = (TextView) mHeaderView.findViewById(R.id.tv_header_state);
        tvHeaderLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_header_last_refresh_time);

        ivHeaderArrow = (ImageView) mHeaderView.findViewById(R.id.iv_header_arrow);



        int height = mHeaderView.getHeight(); // 只有在布局被绘制到界面上时, 才能获取到高度
        // 获取测量的高度
        mHeaderView.measure(0, 0); // 手动测量, 指定 宽高0,0. 表示按其默认设置测量.
        mHeaderViewHeight = mHeaderView.getMeasuredHeight();
        System.out.println("height: " + height  + "measuredHeight: " + mHeaderViewHeight);
        // 设置内边距
        mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

        addHeaderView(mHeaderView);
    }

    // 根据用户手指触摸, 动态的修改mHeaderView的paddingTop
    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();

                break;
            case MotionEvent.ACTION_MOVE:

                // 如果正在刷新中, 返回, 按父类处理
                if(currentState == REFRESHING){
                    return super.onTouchEvent(ev);
                }

                int moveY = (int) ev.getY();

                // 获取Y轴的移动量
                int diffY = moveY - downY;
// System.out.println("diffY: " + diffY);

                // 当前ListView最上边能看到的条目, 是ListView所有条目的第一条. 
                if(diffY > 0 && getFirstVisiblePosition() == 0){
                    // 下拉, 把头布局显示出来
// int paddingTop= - 60 + (moveY - downY) 
                    int paddingTop = - mHeaderViewHeight + diffY / 2;
                    mHeaderView.setPadding(0, paddingTop, 0, 0);

                    if(paddingTop > 0 && currentState != RELEASE_REFRESH){ // 刚刚不是释放刷新模式才进来
                        // 头布局已经完全显示了, 切换成 释放刷新 模式
                        System.out.println("切换 释放刷新 模式");
                        currentState = RELEASE_REFRESH;
                        updateHeaderView();

                    }else if(paddingTop < 0 && currentState != PULL_DOWN){// 刚刚不是下拉刷新模式才进来
                        // 头布局没有完全显示, 切换成 下拉刷新 模式
                        System.out.println("切换成 下拉刷新 模式");
                        currentState = PULL_DOWN;
                        updateHeaderView();
                    }

                    return true; // 子类处理的触摸事件, 不再传递给父类处理
                }

                break;
            case MotionEvent.ACTION_UP:
                // 手指抬起了
                if(currentState == PULL_DOWN){
                    // 抬起时, 是下拉刷新状态, 隐藏
                    mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
                }else if(currentState == RELEASE_REFRESH){
                    // 抬起时, 是释放刷新, 恢复成初始大小
                    mHeaderView.setPadding(0, 0, 0, 0);

                    currentState = REFRESHING;
                    updateHeaderView();
                }

                break;

            default:
                break;
        }

        return super.onTouchEvent(ev);
    }

    /** * 刷新结束时调用 */
    public void onRefreashFinish() {
        if(isLoadMore){
            System.out.println("加载更多完毕");
            // 如果是加载更多, 恢复脚布局
            isLoadMore = false;
            // 隐藏脚布局
            mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);

        }else {
            // 如果是下拉刷新, 恢复头布局
            currentState = PULL_DOWN;
            ivHeaderArrow.setVisibility(View.VISIBLE);
            pbHeaderLoading.setVisibility(View.INVISIBLE);
            tvHeaderState.setText("下拉刷新");
            tvHeaderLastRefreshTime.setText("最后刷新时间: " + getLastRefreshTime());
            // 隐藏头布局
            mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
        }
    }

    // 2015-5-22 11:26:53
    private String getLastRefreshTime() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String str = format.format(new Date());
        return str;
    }

    // private final int PULL_DOWN = 0; // 下拉刷新状态
// private final int RELEASE_REFRESH = 1; // 释放刷新状态
// private final int REFRESHING = 2; // 正在刷新中...
    /** * 根据当前的状态更新 头布局 */
    private void updateHeaderView() {
        switch (currentState) {
        case PULL_DOWN:
            // 更新 下拉刷新状态 时头布局
            // 更新文本
            tvHeaderState.setText("下拉刷新");
            // 执行动画
            ivHeaderArrow.startAnimation(rotateDown);

            break;
        case RELEASE_REFRESH:
            // 更新 释放刷新状态 时头布局
            // 更新文本
            tvHeaderState.setText("释放刷新");
            // 执行动画
            ivHeaderArrow.startAnimation(rotateUp);

            break;
        case REFRESHING:
            // 更新 正在刷新中...时头布局

            ivHeaderArrow.clearAnimation();
            ivHeaderArrow.setVisibility(View.INVISIBLE);

            pbHeaderLoading.setVisibility(View.VISIBLE);
            tvHeaderState.setText("正在刷新中...");

            if(listener != null){
                listener.onRefreshRequest();
            }

            break;

        default:
            break;
        }
    }

    public void setOnRefreshListener(OnRefreshListener listener){
        this.listener = listener;
    }


// public static int SCROLL_STATE_IDLE = 0;
// public static int SCROLL_STATE_TOUCH_SCROLL = 1;
// public static int SCROLL_STATE_FLING = 2;
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

        System.out.println("scrollState");
        // 当状态改变时候调用
        if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING){
            // 如果当前状态是 闲置/滑翔 状态
            // 到最后一条
            int lastVisiblePosition = getLastVisiblePosition();

            System.out.println("lastVisiblePosition: " + lastVisiblePosition);
            if(lastVisiblePosition == getCount() - 1){
                // 显示脚布局
                mFooterView.setPadding(0, 0, 0, 0);

                // 让ListView滚动到最下边
                setSelection(getCount());

                isLoadMore  = true;
                if(listener != null){
                    listener.onLoadMore();
                }
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // 当状滑动时调用
    }

    /** * 刷新接口,监听 * @author poplar * */
    public interface OnRefreshListener{

        void onRefreshRequest();

        void onLoadMore();

    }

}



activity_main:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <com.cy.refreshlistview.ui.RefreshListView
        android:id="@+id/rlv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

layout_list_head:

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

    <FrameLayout  android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" >

        <ImageView  android:id="@+id/iv_header_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/common_listview_headview_red_arrow" />

        <ProgressBar  android:id="@+id/pb_header_loading" android:layout_width="50dp" android:layout_height="50dp" android:indeterminateDrawable="@drawable/red_progress_bar" android:layout_gravity="center" />
    </FrameLayout>

    <LinearLayout  android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" >

        <TextView  android:id="@+id/tv_header_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下拉刷新" android:textColor="#FF0000" android:textSize="18sp" />

        <TextView  android:id="@+id/tv_header_last_refresh_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="最后刷新时间: 2015-5-22 09:30:54" android:textSize="14sp" />
    </LinearLayout>

</LinearLayout>

layout_list_footer:

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

    <ProgressBar  android:id="@+id/pb_header_loading" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center" android:layout_margin="5dp" android:indeterminateDrawable="@drawable/red_progress_bar" />

    <TextView  android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="加载更多..." android:textColor="#FF0000" android:textSize="18sp" />

</LinearLayout>
public class MainActivity extends Activity {

    private List<String> mListData;
    private MyAdapter adapter;
    private RefreshListView rlv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        rlv = (RefreshListView) findViewById(R.id.rlv);

        setData(rlv);

        rlv.setOnRefreshListener(new OnRefreshListener() {

            @Override
            public void onRefreshRequest() {
                System.out.println("用户有刷新请求");
                new Thread(){
                    public void run() {
                        SystemClock.sleep(3000);

                        mListData.add(0, "这是下拉刷新出来的数据");

                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                adapter.notifyDataSetChanged();
                                rlv.onRefreashFinish();
                            }
                        });
                    };
                }.start();
            }

            @Override
            public void onLoadMore() {
                System.out.println("用户有加载更多请求");
                new Thread(){
                    public void run() {
                        SystemClock.sleep(3000);

                        mListData.add("这是加载更多 出来的数据1");
                        mListData.add("这是加载更多 出来的数据2");
                        mListData.add("这是加载更多 出来的数据3");

                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                adapter.notifyDataSetChanged();
                                rlv.onRefreashFinish();
                            }
                        });
                    };
                }.start();


            }
        });

    }

    private void setData(RefreshListView rlv) {
        // 初始化数据
        mListData = new ArrayList<String>();
        for (int i = 0; i < 30; i++) {
            mListData.add("这是一条ListView的数据: " + i);
        }

        // 设置Header, 必须在setAdapter之前设置
// Button button = new Button(this);
// button.setText("这是一个按钮");
// rlv.addHeaderView(button);

        //设置适配器

        adapter = new MyAdapter();
        rlv.setAdapter(adapter);
    }

    class MyAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return mListData.size();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            TextView textView = new TextView(MainActivity.this);
            textView.setTextSize(18);
            textView.setText(mListData.get(position));

            return textView;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

    }

}

参考:android自定义控件

上一篇:3.2 使用 URL 类请求和提交数据详解 下一篇:UICC之UiccCardApplication