Android 精通自定义视图(5)Android ListView 实现上拖累加载的以身作则代码。

项目Demo:https://github.com/liaozhoubei/CustomViewDemo

本文介绍了Android ListView
实现达标拉加载的演示代码,分享给大家,具体如下:

下拉刷新以及加载重多

下拉刷新以及加载重多就片独功能在资讯类的app最轻找到,当您想翻有无发最新的资讯的时段才待把手指在屏幕上于下一致拉就可知创新数据,而加载重多则是拉至眼前页面底部的早晚想找到往期底情,这时手指向上一样拉,往期情就是加载出来。现在便受咱实现这效果吧,效果如下:

亚洲城ca88手机网页版 1

Refreshlist.gif

亚洲城ca88手机网页版 2

贯彻原理

从成效图中剖析,我们可汲取以下结论:

1、整体布局是用ListView实现之

2、上拉加载是以顶部加数据,加载重多就是在脚多数量。

然后我们以翻看ListVew的详细资料,我们得看来它拥有addHeaderView()方法,将数据增长到顶部,同时也保有addFooterView()方法,将数据增长到底层。

今咱们就清楚如何当顶部跟脚添加数据了,但是下拉的时光发下拉属性的布局,上拉加载重多吗起一个布局,这同时是怎么回事呢?

的确,这是一个那个让人迷惑的地方,但是若我们转移一个思路,假而它本就是是当何,但是却受隐形起来也?

答案也是凡如此,ListView先设置头布局到顶部,但是也无以屏幕被显,只有在下拉的下才显得出来,同时于下拉的时候链接网络加载重多多少,这就是兑现加载重多之法则!

整套都分析形成了,现在我们就是来探望代码是如何实现之吧!

咱先分析一下争兑现 ListView 上拉加载。

代码实现下拉刷新

尽管如此实现加载重多的法力是因让Listview,但是ListVew并没有章程落实我们怀念使的具有功能,所以我们好透过继承ListView来促成我们需要的作用。代码如下:

public class RefreshListView extends ListView implements OnScrollListener {

private View mHeaderView; // 头布局
private float downY; // 按下的y坐标
private float moveY; // 移动后的y坐标
private int mHeaderViewHeight; // 头布局高度
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 RotateAnimation rotateUpAnim; // 箭头向上动画
private RotateAnimation rotateDownAnim; // 箭头向下动画
private View mArrowView; // 箭头布局
private TextView mTitleText; // 头布局标题
private ProgressBar pb; // 进度指示器
private TextView mLastRefreshTime; // 最后刷新时间
private OnRefreshListener mListener; // 刷新监听
private View mFooterView; // 脚布局
private int mFooterViewHeight; // 脚布局高度
private boolean isLoadingMore; // 是否正在加载更多

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();
}

/**
 * 初始化头布局, 脚布局 滚动监听
 */
private void init() {
    initHeaderView();
    initAnimation();
    initFooterView();
    setOnScrollListener(this);
}

/**
 * 初始化脚布局
 */
private void initFooterView() {
    mFooterView = View.inflate(getContext(), R.layout.layout_footer_list, null);
    mFooterView.measure(0, 0);
    mFooterViewHeight = mFooterView.getMeasuredHeight();
    // 隐藏脚布局
    mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
    addFooterView(mFooterView);
}

/**
 * 初始化头布局的动画
 */
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); // 动画停留在结束位置

}

/**
 * 初始化头布局
 */
private void initHeaderView() {

    mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
    mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
    pb = (ProgressBar) mHeaderView.findViewById(R.id.pb);
    mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title);
    mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh);

    // 提前手动测量宽高
    mHeaderView.measure(0, 0);// 按照设置的规则测量

    mHeaderViewHeight = mHeaderView.getMeasuredHeight();
    System.out.println(" measuredHeight: " + mHeaderViewHeight);

    // 设置内边距, 可以隐藏当前控件 , -自身高度
    mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

    // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
    addHeaderView(mHeaderView);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {

    // 判断滑动距离, 给Header设置paddingTop
    switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
        downY = ev.getY();
        System.out.println("downY: " + downY);

        break;
    case MotionEvent.ACTION_MOVE:
        moveY = ev.getY();
        System.out.println("moveY: " + moveY);
        // 如果是正在刷新中, 就执行父类的处理
        if (currentState == REFRESHING) {
            return super.onTouchEvent(ev);
        }

        float offset = moveY - downY; // 移动的偏移量
        // 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部
        if (offset > 0 && getFirstVisiblePosition() == 0) {

            // int paddingTop = -自身高度 + 偏移量

            int paddingTop = (int) (-mHeaderViewHeight + offset);
            mHeaderView.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 不完全显示, 恢复
            mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
        } else if (currentState == RELEASE_REFRESH) {
            // - paddingTop >= 0 完全显示, 执行正在刷新...
            mHeaderView.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;
    }
}

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

}
// 将获取的时间格式化
private String getTime() {
    long currentTimeMillis = System.currentTimeMillis();
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return format.format(currentTimeMillis);
}

public interface OnRefreshListener {

    void onRefresh(); // 下拉刷新

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

public void setRefreshListener(OnRefreshListener mListener) {
    this.mListener = mListener;
}

// 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: " + scrollState);
    if (isLoadingMore) {
        return; // 已经在加载更多.返回
    }

    // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多
    if (scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)) {
        isLoadingMore = true;
        System.out.println("scrollState: 开始加载更多");
        mFooterView.setPadding(0, 0, 0, 0);

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

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

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    // 滑动过程
}

}
  • 当我们上拉的时,会油然而生一个提示界面,即 ListView 的 Footer 布局。
  • ListView 要促成滚动,所以要监听 ListView 滚动事件,即
    OnScrollListener() 事件。
  • 当我们开始滚动时,Footer 布局才慢慢显示出来,所以要监听 ListView
    的 onTouch() 事件。

代码分析

一如既往看上去,这其中的代码是甚多之,但是呢仅仅是分为这几乎有的:

1、隐藏头布局(下拉刷新)和底下布局(加载重多)

2、设置触摸事件,在下拉的时刻显得头布局,上拉的早晚显得脚布局

3、在下拉以及上拉之实践的时,我们执行动画,更换头布局以及脚步局里面的上空(箭头、文字等)

4、设置回掉方法,在下拉或上拉的时候以数据传回到,同时更新ListView中之数据

这就是说我们就算照这几有些一个一个之上课吧!

心想事成思路

打开界面的时光隐藏头布局(下拉刷新)

隐藏头布局之代码在偏下办法被:

private void initHeaderView() {

    mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
    mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
    pb = (ProgressBar) mHeaderView.findViewById(R.id.pb);
    mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title);
    mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh);

    // 提前手动测量宽高
    mHeaderView.measure(0, 0);// 按照设置的规则测量
    mHeaderViewHeight = mHeaderView.getMeasuredHeight();
    System.out.println(" measuredHeight: " + mHeaderViewHeight);

    // 设置内边距, 可以隐藏当前控件 , -自身高度
    mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

    // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
    addHeaderView(mHeaderView);
}

是因为藏脚步局的道以及隐藏头布局的计去不很,这里虽主要教学隐藏头布局之措施。

以事先我们询问及ListView中有addHeaderView()方法,将视图或者数额增长到ListVew的顶部,这里就是始于化头布局,然后将该加载到顶部。重要的凡这三尽代码:

    mHeaderView.measure(0, 0);// 按照设置的规则测量
    mHeaderViewHeight = mHeaderView.getMeasuredHeight();        
    mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

怎么要动用measure()方法,这是坐ListView是于Activity执行setContentView()加载了布局之后才后高度与宽度。但是咱以从定义控件被使超前得到控件里面的子布局的可观和幅度,而当measure()传入0这是代表以原的,在XML中设定好的高低获取宽高。

接下来就是setPadding()方法,这个方式是勿是那个熟稔,Padding是安布局的内边距。ListVew的顶部的距离为0,也便是正在屏幕的顶部,而以mHeaderView头布局的Top顶部的比喻设置也负数,那么mHeaderView将无见面当屏幕中显得出来,大家可好新建一个档试试Pading的作用。

上述就是是隐藏头布局的代码,然后将躲藏好之条布局直接长到LisView之中就足以了。

  1. 第一判断 ListView 加载时机,当 ListView 的 lastVisibleItem ==
    totalItemCount 时表示即居于 ListView 最底端,此时允许下拉。
  2. 自从定义一个 FooterView,将 FooterView 添加到 ListView
    底部,在上拉上的亮与就上的隐藏。
  3. 概念一个加载接口,当及带来作完时回调,用于标记状态并加载最新数据开展亮。

安装触摸事件,在下拉的下显得头布局

onTouchEvent()触摸事件中的代码:

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) (-mHeaderViewHeight + offset);
            mHeaderView.setPadding(0, paddingTop, 0, 0);
            if (paddingTop >= 0 && currentState != RELEASE_REFRESH) {// 头布局完全显示
                currentState = RELEASE_REFRESH;
                updateHeader(); // 根据最新的状态值更新头布局内容
            } else if (paddingTop < 0 && currentState != PULL_TO_REFRESH) { // 头布局不完全显示
                currentState = PULL_TO_REFRESH;
                updateHeader(); // 根据最新的状态值更新头布局内容
            }
            return true; // 当前事件被我们处理并消费
        }
        break;
    case MotionEvent.ACTION_UP:
        if (currentState == PULL_TO_REFRESH) {
            mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
        } else if (currentState == RELEASE_REFRESH) {
            mHeaderView.setPadding(0, 0, 0, 0);
            currentState = REFRESHING;
            updateHeader();
        }
        break;

    default:
        break;
    }

当及时段代码中,主要是设置mHeaderView的setPadding()方法,在脚下ListView条目为顶部先是漫漫时虽将该PaddingTop内边距顶部的相距不断的缩减,依据手指运动的距离从负数转为正数,将mHeaderView显示出。

1、定义 Footer

每当下拉的时段显得动画

以触摸事件中产生updateHeader()方法,这是因此来实施选择执行动画与转换文字的一个措施,当我们下来头布局,头布局了亮的上该下箭头变为上箭头,同时更换文字;在出狱下拉,手指离开屏幕的时段,箭头消失,变成循环速度长条

Footer 要兑现之功力:

安装回调方法,更新ListView中之数

最后就是回调方法了,在此地开创了一个Interface接口类OnRefreshListener,这间来个别只措施,一个凡生拉刷新时多多少,一个是加载重多时加数量。

此类似在setRefreshListener中为采用,但咱的RefreshListView自定义控件被运用时,调用setRefreshListener方法,这是将重复写OnRefreshListener接口的点滴独道,在内部传播要多的数据,并且更新适配器,这时RefreshListView就能够实时更新数据了。

第一蹩脚上拉时,Footer
逐渐显示,文字显示为下拉可以加载,箭头向达,进度条隐蔽。

总结

下拉刷新的自定义视图就说到这里就结束了,我们的精通自定义视图也交者即截止了。

虽身为精通自定义视图,但说实话这单是应用层的自定义视图的施用,仍然是基于Android提供的框架中改,如果实在想创立有属于自己之自定义视图,恐怕要上Android底层或者框架层写代码吧!

但是不管怎么样,学习完这几只由定义视图,相信在应用层这方面的自定义视图都未会见生出太死的孤苦了。

每当此处的几篇稿子中,提纲挈领的游说了每个类别里面比较重大之方,关于切实的逻辑并不曾详解,所以马上地方需要大家多看代码,又或直接敲代码,这样才会解这中间说涵盖的学问。

项目Demo:https://github.com/liaozhoubei/CustomViewDemo

扩展阅读:

Android 精通自定义视图(1)
http://www.jianshu.com/p/c2195269ce44

Android 精通自定义视图(2)
http://www.jianshu.com/p/092e126b623f

Android 精通自定义视图(3)
http://www.jianshu.com/p/1660479e76ef

Android 精通自定义视图(4)
http://www.jianshu.com/p/850e387fc9d8

当松开加载的时段,箭头隐藏,进度条显示,文字改吧在加载。

亚洲城ca88手机网页版 3

1、Footer 加载时状态变化

概念一个若达到图所展示之 Footer 的 XML 文件 footer_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
  android:paddingBottom="10dp"
  android:paddingTop="10dp">

  <LinearLayout
    android:id="@+id/layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
      android:id="@+id/tv_tip"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="下拉可以刷新"
      android:textSize="12sp" />
  </LinearLayout>

  <ImageView
    android:id="@+id/img_arrow"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="10dp"
    android:layout_toLeftOf="@+id/layout"
    android:src="@drawable/pull_to_refresh_arrow" />

  <ProgressBar
    android:id="@+id/progress"
    style="@style/progressBar_custom_drawable"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginRight="10dp"
    android:layout_toLeftOf="@+id/img_arrow"
    android:visibility="gone"
    tools:visibility="visible" />
</RelativeLayout>

2、初始化布局

概念一个 RefreshListView 类继承 ListView,重写构造函数,并将 Footer
添加到 ListView 中。

public class RefreshListView extends ListView {
  private View header;
  private int headerHeight;//顶部布局高度
  private int firstVisibleItem;//当前第一个 Item 可见位置
  private float startY;//按下时开始的Y值
  private int scrollState;//当前滚动状态

  private View footer;
  private int footerHeight;//底部布局高度
  private float lastY;
  private boolean canLoadMoreEnabled;//是否允许加载更多

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

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

  public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView(context);
  }


  private void initView(Context context) {
    header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
    footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
    measureView(header);
    measureView(footer);
    //这里获取高度的时候需要先通知父布局header占用的空间
    headerHeight = header.getMeasuredHeight();
    footerHeight = footer.getMeasuredHeight();
    topPadding(-headerHeight);
    bottomPadding(-footerHeight);//用于隐藏 Footer
    this.addHeaderView(header);
    this.addFooterView(footer);
    this.setOnScrollListener(this);
  }

  /**
   * 设置 Footer 布局的下边距
   * 以隐藏 Footer
   * @param topPadding
   */
  private void bottomPadding(int bottomPadding) {
    footer.setPadding(footer.getPaddingLeft(), footer.getPaddingTop(),
        footer.getPaddingRight(),
        bottomPadding);
    footer.invalidate();
  }
}

3、实现达标拉加载

为 ListView 设置监听

public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
  private int firstVisibleItem;//当前第一个 Item 可见位置
  private int scrollState;//当前滚动状态

  private void initView(Context context) {
    header = LayoutInflater.from(context).inflate(R.layout.header_layout, null);
    footer = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);
    measureView(header);
    measureView(footer);
    //这里获取高度的时候需要先通知父布局header占用的空间
    headerHeight = header.getMeasuredHeight();
    footerHeight = footer.getMeasuredHeight();
    topPadding(-headerHeight);
    bottomPadding(-footerHeight);
    this.addHeaderView(header);
    this.addFooterView(footer);
    this.setOnScrollListener(this);
  }

  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    this.scrollState = scrollState;
  }

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

加载的机是判断lastVisibleItem ==
totalItemCount,而上拉事件亚洲城ca88手机网页版我们要重写 onTouchEvent()
事件,首先定义几独状态。

private float lastY;

private static int state;//当前状态
private final static int NONE = 0;//正常状态
private final static int PULL = 1;//下拉状态
private final static int RELEASE = 2;//释放状态
private final static int REFRESHING = 3;//正在刷新状态

在 onTouchEvent 中,在 ACTION_DOWN 时,记录最开始的 Y 值,然后于
ACTION_MOVE 事件中实时记录移动距离 space,不断刷新 FooterView 的
bootomPadding,让它跟滑动距离进行亮,继续滑动,当 space 大于了
FooterHeight 时,状态让啊 RELEASE,表示足纵进行刷新操作。

@Override
  public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //最顶部
        if (firstVisibleItem == 0) {//刷新
          canRefreshEnabled = true;
          startY = ev.getY();
        } else if (lastVisibleItem == totalItemCount) {//加载更多
          canLoadMoreEnabled = true;
          lastY = ev.getY();
        }
        break;
      case MotionEvent.ACTION_MOVE:
        onMove(ev);
        break;
      case MotionEvent.ACTION_UP:
        if (state == RELEASE) {//如果已经释放,则可以提示刷新数据
          state = REFRESHING;
          if (iRefreshListener != null) {
            iRefreshListener.onRefresh();
          }
          if (iLoadMoreListener != null) {
            iLoadMoreListener.onLoadMore();
          }
        } else if (state == PULL) {//如果是在下拉状态,不刷新数据
          state = NONE;
        }
        if (canRefreshEnabled) {
          refreshViewByState();
        }
        if (canLoadMoreEnabled) {
          loadViewByState();
        }
        canLoadMoreEnabled = false;
        canRefreshEnabled = false;
        break;
    }
    return super.onTouchEvent(ev);
  }

  /**
   * 判断移动过程中的操作
   *
   * @param ev
   */
  private void onMove(MotionEvent ev) {
    int tempY = (int) ev.getY();
    int refreshSpace = (int) (tempY - startY);//向下移动的距离
    int topPadding = refreshSpace - headerHeight;//在移动过程中不断设置 topPadding
    int loadSpace = (int) (lastY - tempY);//向上移动的距离
    int bottomPadding = loadSpace - footerHeight;//在移动过程中不断设置 bottomPadding
    switch (state) {
      case NONE:
        //下拉移动距离大于0
        if (refreshSpace > 0) {
          state = PULL; //状态变成下拉状态
          refreshViewByState();
        }
        //上拉移动距离大于0
        if (loadSpace > 0) {
          state = PULL;//状态变成下拉状态
          loadViewByState();
        }
        break;
      case PULL:
        if (canRefreshEnabled) {
          topPadding(topPadding);//在移动过程中不断设置 topPadding,让 Header 随着下拉动作慢慢显示
        }
        if (canLoadMoreEnabled) {
          bottomPadding(bottomPadding);//在移动过程中不断设置 bottomPadding,让 Footer 随着上拉动作慢慢显示
        }
        //移动距离大于headerHeight并且正在滚动
        if (canRefreshEnabled && refreshSpace > (headerHeight + 30) && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
          state = RELEASE;//提示释放
          refreshViewByState();
        }
        //移动距离大于footerHeight并且正在滚动
        if (canLoadMoreEnabled && loadSpace > footerHeight + 30 && scrollState == SCROLL_STATE_TOUCH_SCROLL) {
          state = RELEASE;//提示释放
          loadViewByState();//刷新footer布局
        }
        break;
      case RELEASE:
        if (canRefreshEnabled) {
          topPadding(topPadding);
          //移动距离小于headerHeight
          if (refreshSpace < headerHeight + 30) {
            state = PULL;//提示下拉
          } else if (refreshSpace <= 0) {
            state = NONE;
          }
          refreshViewByState();//更新header
        }
        if (canLoadMoreEnabled) {
          bottomPadding(bottomPadding);
          //移动距离小于footerHeight
          if (loadSpace < footerHeight + 30) {
            state = PULL;//提示下拉
          } else if (loadSpace <= 0) {
            state = NONE;
          }
          loadViewByState();//更新footer
        }
        break;
    }
  }

加载数据的早晚,要基于状态不断更改 FooterView
的展示,箭头定义一个旋转动画让那个随从滑动距离实现转,进度久也设置了逐帧动画实现从定义进度长达。

private void loadViewByState() {
    TextView tip = footer.findViewById(R.id.tv_tip);
    ImageView arrow = footer.findViewById(R.id.img_arrow);
    ProgressBar progressBar = footer.findViewById(R.id.progress);
    progressBar.setBackgroundResource(R.drawable.custom_progress_bar);
    AnimationDrawable animationDrawable = (AnimationDrawable) progressBar.getBackground();
    //给箭头设置动画
    RotateAnimation anim = new RotateAnimation(0, 180,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    RotateAnimation anim1 = new RotateAnimation(180, 0,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f,
        RotateAnimation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(200);
    anim.setFillAfter(true);
    anim1.setDuration(200);
    anim1.setFillAfter(true);
    switch (state) {
      case NONE://正常,footer不显示
        bottomPadding(-footerHeight);
        arrow.clearAnimation();
        break;
      case PULL://下拉状态
        arrow.setVisibility(VISIBLE);//箭头显示,进度条隐藏
        progressBar.setVisibility(GONE);
        if (animationDrawable.isRunning()) {
          animationDrawable.stop();
        }
        tip.setText("上拉可以加载");
        arrow.clearAnimation();
        arrow.setAnimation(anim);//箭头向下
        break;
      case RELEASE://释放状态
        arrow.setVisibility(VISIBLE);//箭头显示,进度条隐藏
        progressBar.setVisibility(GONE);
        if (animationDrawable.isRunning()) {
          //停止动画播放
          animationDrawable.stop();
        }
        tip.setText("松开开始加载");
        arrow.clearAnimation();
        arrow.setAnimation(anim);//箭头向上
        break;
      case REFRESHING://刷新状态
        bottomPadding(50);
        arrow.setVisibility(GONE);//箭头显示,进度条隐藏
        progressBar.setVisibility(VISIBLE);
        animationDrawable.start();
        tip.setText("正在加载...");
        arrow.clearAnimation();
        break;
    }
  }

4、下拉刷新完成回调

当及拉加载成功时,我们用实现多少的基础代谢,并且只要通报 Adapter
刷新数据,这里我们定义一个监听接口实现回调即可。回调在 ACTION_UP 的
RELEASE 状态下开展挂号。

private ILoadMoreListener iLoadMoreListener;

  public void setILoadMoreListener(ILoadMoreListener iLoadMoreListener) {
    this.iLoadMoreListener = iLoadMoreListener;
  }

  public interface ILoadMoreListener {
    void onLoadMore();
  }

5、测试

package com.dali.refreshandloadmorelistview;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

import java.util.ArrayList;

public class MainActivity extends Activity implements RefreshListView.IRefreshListener, RefreshListView.ILoadMoreListener {

  private ArrayList<ApkEntity> apk_list;
  private ListAdapter adapter;
  private RefreshListView listView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setData();
    showList(apk_list);
  }

  private void showList(ArrayList<ApkEntity> apk_list) {
    if (adapter == null) {
      listView = (RefreshListView) findViewById(R.id.listview);
      listView.setIRefreshListener(this);
      listView.setILoadMoreListener(this);
      adapter = new ListAdapter(this, apk_list);
      listView.setAdapter(adapter);
    } else {
      adapter.onDateChange(apk_list);
    }
  }

  private void setData() {
    apk_list = new ArrayList<ApkEntity>();
    for (int i = 0; i < 10; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默认数据" + i);
      entity.setDes("这是一个神奇的应用");
      entity.setInfo("50w用户");
      apk_list.add(entity);
    }
  }

  private void setRefreshData() {
    for (int i = 0; i < 2; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默认数据 + 刷新" + i);
      entity.setDes("这是一个神奇的应用");
      entity.setInfo("50w用户");
      apk_list.add(0, entity);
    }
  }

  private void setLoadData() {
    for (int i = 0; i < 2; i++) {
      ApkEntity entity = new ApkEntity();
      entity.setName("默认数据 + 加载" + (adapter.getCount() + i));
      entity.setDes("这是一个神奇的应用");
      entity.setInfo("50w用户");
      apk_list.add(entity);
    }
  }

  @Override
  public void onRefresh() {
    //添加刷新动画效果
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        //获取最新数据
        setRefreshData();
        //通知界面显示数据
        showList(apk_list);
        //通知 ListView 刷新完成
        listView.refreshComplete();
      }
    }, 2000);
  }

  @Override
  public void onLoadMore() {
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
      @Override
      public void run() {
        //获取最新数据
        setLoadData();
        //通知界面显示数据
        showList(apk_list);
        //通知 ListView 刷新完成
        listView.loadMoreComplete();
      }
    }, 2000);
  }
}

GitHub 源码

以上就是本文的全部内容,希望对大家之读抱有助,也盼望大家多支持脚本的家。

您可能感兴趣的文章:

  • android使用SwipeRefreshLayout实现ListView下拉刷新上拉加载
  • android使用PullToRefresh框架实现ListView下拉刷新上拉加载重多
  • Android通过XListView实现达标拖累加载下拉刷新功能
  • XListView实现下拉刷新以及高达拉加载原理分析
  • react-native
    ListView下拉刷新上拉加载实现代码
  • Android中Listview下拉刷新以及达成拖累加载重多的又兑现方案
  • Android
    ListView实现达标拖累加载下拉刷新和滑动删除功能
  • Android自定义listview布局实现达标拖累加载下拉刷新功能
  • Android
    XListView下拉刷新以及达标拉加载重多
  • Android实现上拖累加载重多ListView(PulmListView)

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图