教你自定义ListView实现下拉刷新,上拉加载更多

虽然listview 快被RecyclerView取代了,但作为新人的你有必要知道。网上其实已经有很多关于listview上下拉刷新的例子,今天想带大家写一个快速实现 并且简单例子。

  • 我先上几张效果图吧
    这里写图片描述这里写图片描述这里写图片描述这里写图片描述

ok 我先说一下大体思路,

  • 我们先分别写出头布局和脚布局,稍后用 addHeaderView(headView); addFootView(footView);加载到自定义的布局中,默认全部都隐藏
  • 根据我们下滑的状态,来分别对应显示下拉 和上拉刷新的状态
  • 在自定义的listiview,中设置回调 用于状态显示完之后数据的显示

  • 我把主要的代码列出来

package com.example.administrator.listview_demo;

import android.annotation.TargetApi;
import android.content.Context;
import android.icu.text.SimpleDateFormat;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * Created by Xavier on 2016/9/27.
 */
public class RefreshListView extends ListView implements AbsListView.OnScrollListener{
    private  View footView;
    private View headView;
    private float downY; // 按下的y坐标
    private float moveY; // 移动后的y坐标
    private View mArrowView;        // 箭头布局
    private TextView mTitleText;    // 头布局标题
    private ProgressBar pb;         // 进度指示器
    private TextView mLastRefreshTime; // 最后刷新时间
    private Context mContext;
   private int measuredHeighthead; //头布局高度
    private  int measuredHeightfoot; //脚布局
    private RotateAnimation rotateUpAnim; // 箭头向上动画
    private RotateAnimation rotateDownAnim; // 箭头向下动画
    public static final int PULL_TO_REFRESH = 0;// 下拉刷新
    public static final int RELEASE_REFRESH = 1;// 释放刷新
    public static final int REFRESHING = 2; // 刷新中
    private int currentState = PULL_TO_REFRESH; // 当前刷新模式
    private boolean isLoadingMore; // 是否正在加载更多
    private OnRefreshListener mListener; // 刷新监听

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

    public RefreshListView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext=context;
        init();
    }

    private void init() {
        initAnimation();
        initHead();
        initFoot();
        setOnScrollListener(this);
    }
    private void initHead() {
         headView = View.inflate(mContext, R.layout.layout_header_list, null);
        mArrowView = headView.findViewById(R.id.iv_arrow);
        pb = (ProgressBar) headView.findViewById(R.id.pb);
        mTitleText = (TextView) headView.findViewById(R.id.tv_title);
        mLastRefreshTime = (TextView) headView.findViewById(R.id.tv_desc_last_refresh);
        headView.measure(0,0);
        measuredHeighthead = headView.getMeasuredHeight();
        headView.setPadding(0,-measuredHeighthead,0,0);
        addHeaderView(headView);
  }

    private void initFoot() {
          footView = View.inflate(mContext, R.layout.layout_footer_list, null);
         footView.measure(0,0);
         measuredHeightfoot = footView.getMeasuredHeight();
         footView.setPadding(0,-measuredHeightfoot,0,0);
         addFooterView(footView);
    }
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                downY=ev.getY();

                break;
            case  MotionEvent.ACTION_MOVE:

                moveY=ev.getY();
                if(currentState == REFRESHING){
                    return super.onTouchEvent(ev);
                }

                float offset = moveY - downY; // 移动的偏移量
                if(offset > 0 && getFirstVisiblePosition() == 0){
                    //int paddingTop = -自身高度 + 偏移量
                    int paddingTop = (int) (- measuredHeighthead + offset);
                   headView.setPadding(0, paddingTop, 0, 0);

                    if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示
                        System.out.println("切换成释放刷新模式: " + paddingTop);
                        // 切换成释放刷新模式
                        currentState = RELEASE_REFRESH;
                       updateHeader(); // 根据最新的状态值更新头布局内容
                    }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示
                        System.out.println("切换成下拉刷新模式: " + paddingTop);
                        // 切换成下拉刷新模式
                        currentState = PULL_TO_REFRESH;
                      updateHeader(); // 根据最新的状态值更新头布局内容
                    }
                    return  true;
                }
                break;
            case  MotionEvent.ACTION_UP:
                // 根据刚刚设置状态
                if(currentState == PULL_TO_REFRESH){
//          - paddingTop < 0 不完全显示, 恢复
                   headView.setPadding(0, -measuredHeighthead, 0, 0);
                }else if(currentState == RELEASE_REFRESH) {
//          - paddingTop >= 0 完全显示, 执行正在刷新...
                    headView.setPadding(0, 0, 0, 0);
                    currentState = REFRESHING;
                    updateHeader();
                }
                break;
            default:
                break;
        }



        return super.onTouchEvent(ev);
    }

    /**
     * 根据状态更新头布局内容
     */
    private void updateHeader() {
        switch (currentState) {
            case PULL_TO_REFRESH: // 切换回下拉刷新
                // 做动画, 改标题
                mArrowView.startAnimation(rotateDownAnim);
                mTitleText.setText("下拉刷新");

                break;
            case RELEASE_REFRESH: // 切换成释放刷新
                // 做动画, 改标题
                mArrowView.startAnimation(rotateUpAnim);
                mTitleText.setText("释放刷新");

                break;
            case REFRESHING: // 刷新中...
                mArrowView.clearAnimation();
                mArrowView.setVisibility(View.INVISIBLE);
                pb.setVisibility(View.VISIBLE);
                mTitleText.setText("正在刷新中...");

                if(mListener != null){
                    mListener.onRefresh(); // 通知调用者, 让其到网络加载更多数据.
                }

                break;

            default:
                break;
        }
    }
    /**
     * 初始化头布局的动画
     */
    private void initAnimation() {
        // 向上转, 围绕着自己的中心, 逆时针旋转0 -> -180.
        rotateUpAnim = new RotateAnimation(0f, -180f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateUpAnim.setDuration(300);
        rotateUpAnim.setFillAfter(true); // 动画停留在结束位置

        // 向下转, 围绕着自己的中心, 逆时针旋转 -180 -> -360
        rotateDownAnim = new RotateAnimation(-180f, -360,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateDownAnim.setDuration(300);
        rotateDownAnim.setFillAfter(true); // 动画停留在结束位置

    }
    @TargetApi(Build.VERSION_CODES.N)
    private String getTime() {
        long currentTimeMillis = System.currentTimeMillis();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(currentTimeMillis);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多
        if(isLoadingMore){
            return; // 已经在加载更多.返回
        }
        if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){
            isLoadingMore = true;
            System.out.println("scrollState: 开始加载更多");
            footView.setPadding(0, 0, 0, 0);

            setSelection(getCount()); // 跳转到最后一条, 使其显示出加载更多.

            if(mListener != null){
                mListener.onLoadMore();
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }
    public interface OnRefreshListener{

        void onRefresh(); // 下拉刷新

        void onLoadMore();// 加载更多
    }

    public void setRefreshListener(OnRefreshListener mListener) {
        this.mListener = mListener;
    }
    /**
     * 刷新结束, 恢复界面效果
     */
    public void onRefreshComplete() {
        if(isLoadingMore){
            // 加载更多
            footView.setPadding(0, -measuredHeightfoot, 0, 0);
            isLoadingMore = false;
        }else {
            // 下拉刷新
            currentState = PULL_TO_REFRESH;
            mTitleText.setText("下拉刷新"); // 切换文本
            headView.setPadding(0, -measuredHeighthead, 0, 0);// 隐藏头布局
            pb.setVisibility(View.INVISIBLE);
            mArrowView.setVisibility(View.VISIBLE);
            String time = getTime();
            mLastRefreshTime.setText("最后刷新时间: " + time);
        }
    }
}


欢迎入我的qq群 讨论 312327703

附上源码点击可下载

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值