美文网首页
自定义控件之-----Listview

自定义控件之-----Listview

作者: 我是你森哥哥 | 来源:发表于2017-06-08 14:38 被阅读0次
    puto.gif

    继承Listview,增强Listview的功能

    实现下拉刷新的功能

    1、继承Listview

    
            public class RefreshListView extends ListView {
    
                private int downY;
                private View header;
                private int headerHeight;
                public RefreshListView(Context context, AttributeSet attrs) {
                    super(context, attrs);
                }
            }
    
    

    2、添加头布局,关键方法addHeaderView

    
    
            public RefreshListView(Context context, AttributeSet attrs) {
                super(context, attrs);
                initHeader();
            }
    
            private void initHeader() {
                header = View.inflate(getContext(), R.layout.refresh_header, null);
                // 把布局添加到Listview的头上
                this.addHeaderView(header);
            }
    
    

    3、隐藏头布局,关键方法setPadding

    
            private void initHeader() {
                header = View.inflate(getContext(), R.layout.refresh_header, null);
                // 隐藏头布局
                // 主动测量控件,获取测量的宽高
                header.measure(0, 0);// 把布局中的宽高给测量出来
                // 获取测量的宽高
                headerHeight = header.getMeasuredHeight();
                header.setPadding(0, -headerHeight, 0, 0);
                // 把布局添加到Listview的头上
                this.addHeaderView(header);
            }
    
    

    4、处理事件,让头布局随手指移动,关键方法setPadding,计算手指移动的距离,再计算出头布局要设置的顶部padding值,通过setPadding方法达到移动头布局的效果

    
    
            // 处理触摸事件
            @Override
            public boolean onTouchEvent(MotionEvent ev) {
                switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = (int) ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int moveY = (int) ev.getY();
                    // 计算手指移动的距离
                    int diffY = moveY - downY;
                    // 只处理从上往下的事件
                    if(diffY>0){
                        // 计算头布局距离顶部的padding值
                        int topPadding = diffY - headerHeight;
                        header.setPadding(0, topPadding, 0, 0);
                        return true;// 自己处理的从上往下的触摸事件,需要消费掉
                    }
                    break;
        
                default:
                    break;
                }
                return super.onTouchEvent(ev);
            }
    
    

    5、当Listview第一个条目没有完全展示时,给头布局设置padding没有效果,需要判断当第一个条目完全展示时,才处理下拉刷新

    
    
            // 处理触摸事件
            @Override
            public boolean onTouchEvent(MotionEvent ev) {
                switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = (int) ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int moveY = (int) ev.getY();
                    // 计算手指移动的距离
                    int diffY = moveY - downY;
                    // 只有当Listview中的第一个条目完全展示时,header.setPadding才有效果,才能自己处理事件
                    if (getFirstVisiblePosition() != 0) {
                        // 如果自己不处理事件,每次移动需要给downY重新赋值
                        downY = (int) ev.getY();
                        break;
                    }
                    // 只处理从上往下的事件
                    if(diffY>0){
                        // 计算头布局距离顶部的padding值
                        int topPadding = diffY - headerHeight;
                        header.setPadding(0, topPadding, 0, 0);
                        return true;// 自己处理的从上往下的触摸事件,需要消费掉
                    }
                    break;
        
                default:
                    break;
                }
                return super.onTouchEvent(ev);
            }
    

    6、根据手指移动的距离,设置刷新状态

    
    
            private static final int PULLREFRESH_STATE = 1;// 下拉刷新状态
            private static final int RELEASE_STATE = 2;// 松开刷新状态
            private static final int REFRESHING_STATE = 3;// 正在刷新状态
            private int current_state = PULLREFRESH_STATE;// 当前刷新状态
    
            case MotionEvent.ACTION_MOVE:
                int moveY = (int) ev.getY();
                // 计算手指移动的距离
                int diffY = moveY - downY;
                // 只有当Listview中的第一个条目完全展示时,header.setPadding才有效果,才能自己处理事件
                if (getFirstVisiblePosition() != 0) {
                    // 如果自己不处理事件,每次移动需要给downY重新赋值
                    downY = (int) ev.getY();
                    break;
                }
                // 只处理从上往下的事件
                if (diffY > 0) {
                    // 计算头布局距离顶部的padding值
                    int topPadding = diffY - headerHeight;
                    // 根据toppadding值是否大于0 头布局是否完全展示,判断状态的切换
                    if (topPadding >= 0 && current_state != RELEASE_STATE) {// 头布局完全展示,切换到松开刷新
                                                                            // ,如果已经是松开刷新状态,就不用再切换
                        current_state = RELEASE_STATE;
                        System.out.println("切换到松开刷新");
                        switchState();
                    } else if (topPadding < 0 && current_state != PULLREFRESH_STATE) {// 头布局没有完全展示,切换到下拉刷新
                        current_state = PULLREFRESH_STATE;
                        System.out.println("切换到下拉刷新");
                        switchState();
                    }
    
                    header.setPadding(0, topPadding, 0, 0);
                    return true;// 自己处理的从上往下的触摸事件,需要消费掉
                }
                break;
            case MotionEvent.ACTION_UP:
                // 手指抬起时,根据当前的状态判断是否切换到正在刷新
                if (current_state == PULLREFRESH_STATE) {// 抬起时,是下拉刷新,头布局没有完全展示,不切换到正在刷新
                    // 隐藏头布局
                    header.setPadding(0, -headerHeight, 0, 0);
                } else if (current_state == RELEASE_STATE) {// 抬起时,是松开刷新,切换到正在刷新
                    current_state = REFRESHING_STATE;
                    // 让头布局正好完全展示
                    header.setPadding(0, 0, 0, 0);
                    System.out.println("切换到正在刷新");
                    switchState();
                }
                break;
    
            * 箭头动画
            
            public RefreshListView(Context context, AttributeSet attrs) {
                super(context, attrs);
                initHeader();
                initAnimation();
            }
    
            private void initAnimation() {
                up = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
                        Animation.RELATIVE_TO_SELF, 0.5f);
                up.setDuration(200);
                up.setFillAfter(true);
                down = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF,
                        0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                down.setDuration(200);
                down.setFillAfter(true);
            }
    
            * 根据状态更新控件
    
            // 切换状态时,更新界面
            private void switchState() {
                switch (current_state) {
                case PULLREFRESH_STATE:
                    state.setText("下拉刷新");
                    progress.setVisibility(View.INVISIBLE);
                    arrow.setVisibility(View.VISIBLE);
                    arrow.startAnimation(down);
                    break;
                case RELEASE_STATE:
                    state.setText("松开刷新");
                    arrow.startAnimation(up);
                    break;
                case REFRESHING_STATE:
                    // 由于动画设置了setFillAfter 控件就停留在结束时的效果
                    arrow.clearAnimation();
                    state.setText("正在刷新");
                    progress.setVisibility(View.VISIBLE);
                    arrow.setVisibility(View.INVISIBLE);
                    break;
        
                default:
                    break;
                }
            }
    
    

    7、暴露接口,让外界实现业务

    
            // 对外暴露接口
            public interface OnRefreshListener {
                // 正在刷新时,回调
                void onRefreshing();
            }
        
            // 提供传递监听器的方法
            public void setOnRefreshListener(OnRefreshListener listener) {
                this.mListener = listener;
            }
    
            case MotionEvent.ACTION_UP:
                // 手指抬起时,根据当前的状态判断是否切换到正在刷新
                if (current_state == PULLREFRESH_STATE) {// 抬起时,是下拉刷新,头布局没有完全展示,不切换到正在刷新
                    // 隐藏头布局
                    header.setPadding(0, -headerHeight, 0, 0);
                } else if (current_state == RELEASE_STATE) {// 抬起时,是松开刷新,切换到正在刷新
                    current_state = REFRESHING_STATE;
                    // 让头布局正好完全展示
                    header.setPadding(0, 0, 0, 0);
                    System.out.println("切换到正在刷新");
                    switchState();
                    // 当处于正在刷新状态时,回调监听器的onRefreshing
                    if (mListener != null) {
                        mListener.onRefreshing();
                    }
                }
                break;
    
            * 外界监听刷新状态,处理业务
    
            listview.setOnRefreshListener(new MyListener());
    
            class MyListener implements OnRefreshListener{
    
                @Override
                public void onRefreshing() {
                    // 处理业务
                    new Thread(){
                        public void run() {
                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            runOnUiThread(new Runnable() {
                                
                                @Override
                                public void run() {
                                    arrayList.add(0, "我是拉出来的");
                                    adapter.notifyDataSetChanged();
                                    // 刷新完成后,调用恢复下拉刷新控件的方法
                                    listview.refreshFinished();
                                }
                            });
                        };
                    }.start();
                }
            }
    
    

    8、刷新业务完成后,恢复下拉刷新状态

    
    
            // 下拉刷新完成后,恢复状态,隐藏头布局
            public void refreshFinished() {
                header.setPadding(0, -headerHeight, 0, 0);
                state.setText("下拉刷新");
                progress.setVisibility(View.INVISIBLE);
                arrow.setVisibility(View.VISIBLE);
                current_state = PULLREFRESH_STATE;
            }
    

    实现上拉加载更多功能

    流程:

        1、添加脚布局,addFooterView
        2、隐藏脚布局,setPadding
        3、监听Listview的滚动状态,当处于停止或惯性停止状态时,而且Listview最后一个条目完全展示,才加载更多
        4、对外暴露接口,让外界处理加载更多的业务
        5、加载更多业务完成后,恢复加载更多状态
    

    2.1、添加脚布局

    
    
            public RefreshListView(Context context, AttributeSet attrs) {
                super(context, attrs);
                initHeader();
                initAnimation();
                initFooter();
            }
    
            private void initFooter() {
                footer = View.inflate(getContext(), R.layout.refresh_footer, null);
                // 添加脚布局
                addFooterView(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:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="加载更多中。。。"
                    android:textColor="#f00"
                    android:textSize="25sp" />
            
            </LinearLayout>
    

    2.2、隐藏脚布局

    
    
            private void initFooter() {
                footer = View.inflate(getContext(), R.layout.refresh_footer, null);
                footer.measure(0, 0);
                footerHeight = footer.getMeasuredHeight();
                footer.setPadding(0, 0, 0, -footerHeight);
                // 添加脚布局
                addFooterView(footer);
            }
    
    
    

    2.3、监听Listview滚动状态,根据状态判断是否显示加载更多脚布局

    
            private void initFooter() {
                footer = View.inflate(getContext(), R.layout.refresh_footer, null);
                footer.measure(0, 0);
                footerHeight = footer.getMeasuredHeight();
                footer.setPadding(0, 0, 0, -footerHeight);
                // 添加脚布局
                addFooterView(footer);
                // 监听Listview的滚动状态
                this.setOnScrollListener(new MyOnScrollListener());
            }
    
            class MyOnScrollListener implements OnScrollListener {
                // 状态发生变化时调用
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    // 当处于停止或惯性停止状态时
                    if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                            || scrollState == OnScrollListener.SCROLL_STATE_FLING) {
                        // 而且Listview最后一个条目完全展示
                        if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
                            isLoadMore = true;
                            // 显示加载更多布局
                            footer.setPadding(0, 0, 0, 0);
                            System.out.println("加载更多了");
                            // 自动显示加载更多布局
                            setSelection(getCount());
                        }
                    }
                }
            }
    
    
    

    2.4、暴露接口,让外界处理加载更多业务

    
            // 对外暴露接口
            public interface OnRefreshListener {
                // 正在刷新时,回调
                void onRefreshing();
                // 加载更多时,回调
                void onLoadingMore();
            }
    
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // 当处于停止或惯性停止状态时
                if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                        || scrollState == OnScrollListener.SCROLL_STATE_FLING) {
                    // 而且Listview最后一个条目完全展示
                    if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
                        isLoadMore = true;
                        // 显示加载更多布局
                        footer.setPadding(0, 0, 0, 0);
                        System.out.println("加载更多了");
                        // 自动显示加载更多布局
                        setSelection(getCount());
                        // 当处于加载更多时,调用监听器的onLoadingMore方法
                        if(mListener!=null){
                            mListener.onLoadingMore();
                        }
                    }
                }
            }
            
            
    

    2.5外界处理加载更多业务

    
    
            @Override
            public void onLoadingMore() {
                // 处理业务
                            new Thread(){
                                public void run() {
                                    try {
                                        Thread.sleep(3000);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    runOnUiThread(new Runnable() {
                                        
                                        @Override
                                        public void run() {
                                            arrayList.add("我是加载出来的");
                                            arrayList.add("我是加载出来的");
                                            adapter.notifyDataSetChanged();
                                            // 加载更多完成后,调用控件恢复状态的方法
                                            listview.loadMoreFinished();
                                        }
                                    });
                                };
                            }.start();
            }
    
    
    
    

    2. 6、外界处理完业务,恢复加载更多状态

    
    
            // 加载更多完成后,恢复状态
            public void loadMoreFinished(){
                isLoadMore = false;
                footer.setPadding(0, 0, 0, -footerHeight);
            }
    
    

    github地址

    https://github.com/zssAndroid/RefreshListView/tree/master

    相关文章

      网友评论

          本文标题:自定义控件之-----Listview

          本文链接:https://www.haomeiwen.com/subject/vhxtqxtx.html