自定义ListView下拉刷新效果

实现思路就是自己写一个刷新效果的布局,然后把这个布局定义为ListView的头,监听手势的滑动效果,分别更新不同的UI。

效果图如下:
在这里插入图片描述
直接上代码,代码是完整的,除了那个箭头的图片需要你自己提供,其它的都可以用我的代码,然后你看懂之后,自己在改一下就好了,我写了很多注释。

view_header.xml(自定义刷新的显示内容)
此处一定要在顶层布局加上

  android:gravity="bottom"

这样滑动时的效果才会和我的效果图一致。

<?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="bottom"
    android:orientation="horizontal">

    <LinearLayout
        android:id="@+id/ll_content"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:gravity="center">

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

            <ImageView
                android:id="@+id/iv_head_view_arrow"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:src="@drawable/down" />

            <ProgressBar
                android:id="@+id/pb_head_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="invisible" />
        </FrameLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:gravity="center_horizontal"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_view_header_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:text="下拉刷新"
                android:textColor="#F00"
                android:textSize="25sp" />

            <TextView
                android:id="@+id/tv_view_header_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:singleLine="true"
                android:text="最后刷新时间: 2015-10-11 09:20:35"
                android:textColor="#666"
                android:textSize="14sp" />
        </LinearLayout>
    </LinearLayout>


</LinearLayout>

MyHeaderView.class

public class MyHeaderView extends LinearLayout {

    private ImageView header_arrow;
    private ProgressBar header_progressBar;
    private TextView header_hint_txt;
    private TextView header_time;
    private RotateAnimation animation_up;
    private RotateAnimation animation_down;
    /**
     * 初始状态---正常状态
     */
    public static final int STATE_NORMAL = 1;
    /**
     * 准备刷新状态---松开刷新状态
     */
    public static final int STATE_READY = 2;
    /**
     * 正在刷新
     */
    public static final int STATE_REFRESHING = 3;
    /**
     * 状态值
     */
    private int mState = STATE_NORMAL;
    private View headerView;


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

    public MyHeaderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    // 初始化控件
    private void init() {
        headerView = View.inflate(getContext(), R.layout.view_header, null);
        // 设置初始的布局参数
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
        headerView.setLayoutParams(params);
        this.addView(headerView);

        header_arrow = headerView.findViewById(R.id.iv_head_view_arrow);
        header_progressBar = headerView.findViewById(R.id.pb_head_view);
        header_hint_txt = headerView.findViewById(R.id.tv_view_header_title);
        header_time = headerView.findViewById(R.id.tv_view_header_time);

        // 初始化向上旋转动画
        animation_up = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation_up.setDuration(100);
        animation_up.setFillAfter(true);
        // 初始化向下旋转动画
        animation_down = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation_down.setDuration(100);
        animation_down.setFillAfter(true);

    }

    public void setState(int state) {
        // mState表示上一次的状态值
        if (state == mState) {
            return;
        }
        if (state == STATE_REFRESHING) {
            header_progressBar.setVisibility(View.VISIBLE);
            header_arrow.setVisibility(View.INVISIBLE);
            header_arrow.clearAnimation();
        } else {
            header_progressBar.setVisibility(View.INVISIBLE);
            header_arrow.setVisibility(View.VISIBLE);
        }

        switch (state) {
            case STATE_NORMAL:
                if (mState == STATE_REFRESHING) {
                    // 如果之前是正在刷新
//                    header_arrow.startAnimation(animation_down);
                } else if (mState == STATE_READY) {
                    // 如果之前是准备刷新
                    header_arrow.startAnimation(animation_down);
                }
                header_hint_txt.setText("下拉刷新...");
                break;
            case STATE_REFRESHING:
                header_hint_txt.setText("正在刷新...");
                break;
            case STATE_READY:
                header_arrow.startAnimation(animation_up);
                header_hint_txt.setText("松开刷新...");
                break;
        }
        // 状态置位
        mState = state;
    }

    /**
     * 设置当前headerView的可见高度
     * @param height
     */
    public void setVisibleHeight(int height) {
        // 生成布局参数
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, height);
        headerView.setLayoutParams(layoutParams);
    }

    /**
     * 获取当前headerView的可见高度
     * @return
     */
    public int getVisibleHeight() {
        return headerView.getLayoutParams().height;
    }

}

MyListView.class

public class MyListView extends ListView implements AbsListView.OnScrollListener {

    private MyHeaderView myHeaderView;
    private int header_height;
    private float lastY = -1;
    private int firstVisbleItem;
    private float ratio=1.8f;
    private OnMyListViewListener listener;

    public MyListView(Context context) {
        super(context);
        initHeadView();
        // 对MyListView设置滑动监听事件
        this.setOnScrollListener(this);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initHeadView();
        // 对MyListView设置滑动监听事件
        this.setOnScrollListener(this);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initHeadView();
        // 对MyListView设置滑动监听事件
        this.setOnScrollListener(this);
    }

    private void initHeadView() {
        myHeaderView = new MyHeaderView(getContext());
        // 添加headerView
        this.addHeaderView(myHeaderView);
        final View header_content = myHeaderView.findViewById(R.id.ll_content);
        // 直接获取控件高度,获取不到
//        header_content.getHeight();
        /**
         * 给布局添加监听,当视图可见状态改变的时候,回调到onGlobalLayout()
         */
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                header_height = header_content.getHeight();
                getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: // 按下
                if (lastY == -1) {
                    // 获取当前按下的y坐标
                    lastY = ev.getRawY();
                }
                break;
            case MotionEvent.ACTION_MOVE: // 滑动
                // 手指滑动的距离 = 滑动的y减去按下的y坐标
                float gapY = ev.getRawY() - lastY;
                if (firstVisbleItem == 0 && (gapY > 0 || myHeaderView.getVisibleHeight() > 0)) {
                    updateHeaderHeight(gapY/ratio);
                }
                // 将当前的y坐标赋值给上一次
                lastY = ev.getRawY();
                break;
            case MotionEvent.ACTION_UP: // 放开
            case MotionEvent.ACTION_CANCEL:
                lastY = -1;
                if (firstVisbleItem == 0) {
                    // 如果当前手指抬起时,headerView的可见高度大于headerView的高度,正在刷新
                    if (myHeaderView.getVisibleHeight() > header_height) {
                        myHeaderView.setState(MyHeaderView.STATE_REFRESHING);
                        if (listener != null) {
                            listener.setRefreshing();
                        }
                    }
                        // 重置headerView的可见高度
                        resetHeaderViewHeight();
                }
                break;
            default:
                break;
        }

        return super.onTouchEvent(ev);
    }

    /**
     * 重置可视高度
     */
    private void resetHeaderViewHeight() {
        if (firstVisbleItem == 0) {
            if (myHeaderView.getVisibleHeight() <= 0) {
                return;
            }
            if (myHeaderView.getVisibleHeight() > header_height) {
                myHeaderView.setVisibleHeight(header_height);
            } else {
                myHeaderView.setVisibleHeight(0);
            }
        }
    }


    /**
     * 更新头的高度
     *
     * @param gapY
     */
    private void updateHeaderHeight(float gapY) {
        if (firstVisbleItem == 0) {
            myHeaderView.setVisibleHeight((int) (myHeaderView.getVisibleHeight() + gapY));
//            Log.d(TAG,(int) (myHeaderView.getVisibleHeight() + gapY)+"" );
            // 当头部可视的高度大于headerView的高度时,状态为ready状态
            if (myHeaderView.getVisibleHeight() > header_height) {
                myHeaderView.setState(MyHeaderView.STATE_READY);
            } else {
                myHeaderView.setState(MyHeaderView.STATE_NORMAL);
            }
        }
        // 设置当前选中的条目索引
        setSelection(0);
    }

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

    }

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

    public void setOnMyListViewListener(OnMyListViewListener listener) {
        this.listener = listener;
    }

    public interface OnMyListViewListener {
        void setRefreshing();
    }

    /**
     * 当刷新结束之后,重置headerView的高度
     */
    public void setRefreshingFinish() {
        resetHeaderViewHeight();
    }
}

RefreshActivity.class(演示效果的活动)

public class RefreshActivity extends AppCompatActivity implements MyListView.OnMyListViewListener{

    private MyListView listView;
    private Handler handler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_refresh);
        listView = findViewById(R.id.lv_refresh_list);
        listView.setOnMyListViewListener(this);
        listView.setAdapter(new RefreshListAdapter(this));
    }

    @Override
    public void setRefreshing() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //重置高度
                listView.setRefreshingFinish();
            }
        }, 2000);
    }
}

这个活动的xml我就不贴出来了,因为里面就放了一个组件,就是这个自定义的ListView,还有RefreshListAdapter的代码我也不贴出来了,就是继承BaseAdapter,然后加载了一个自己写的布局,这个布局如效果图所示,就一个TextView。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值