Android View - 上拉刷新下拉加载ListView

Android | 同时实现上拉刷新和下拉加载 | 基于SwipeRefreshLayout 和 ListView 文章目录实现效果1. 准备环境2. 布局2.1 foot_view2.2 activity_main3.代码实现3.1 全局变量3.2 OnCreate3.3 自定义的showInfo方法3.4 下拉刷新3.5 上拉加载4. 所有代码 实现效果 下拉刷新 上拉加载 1. 准备环境 Android Studio 2020.3.1 (1) 给项目安装 androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-beta01布局组件 (2) 在 depen 阅读详情

虽然网上有很多上拉刷新库,效果也很好,只是有时候突然来的Bug不是很好处理,而且有时候还达不到效果,所以今天用ListView实现上拉刷新,下拉加载的效果。直接上码:

本着易扩展的理念,先写一个父类,实现上拉刷新,下拉加载的效果:

package com.johan.library.viewtoolkit.refreshlistview;

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by johan on 2017/7/27.
 */

public abstract class AbsRefreshListView extends ListView implements AbsListView.OnScrollListener {

    /** 动画每毫秒移动距离 */
    private static final int ANIMATION_STEP = 1;

    /** 移动速率 */
    private static final float MOVE_RATIO = 0.4f;

    /** 刷新View最大高度 */
    private static final int REFRESH_VIEW_MAX_HEIGHT = 500;

    /** 不没有刷新 */
    private static final int STATE_NO_REFRESH = 1;
    /** 正在上拉或者下拉 */
    private static final int STATE_RELEASE_REFRESH = 2;
    /** 正在刷新 */
    private static final int STATE_REFRESHING = 3;

    /** 状态 */
    private int state = STATE_NO_REFRESH;

    /** 记录手指滑动的Y值 */
    private float currentY;

    /** headerView和footerView的原始值 */
    private int headerHeight, footerHeight;

    /** Header Footer View */
    private View headerView, footerView;

    /** 记录是否最顶和最底 */
    private boolean isTop = true, isBottom;

    /** 监听滑动的Listener,因为在AbsRefreshListView已经调用了setOnScrollListener,需要以另一种方式提供用户使用 */
    private OnScrollListener onScrollListener;

    /** 是否可以上拉和下拉 */
    private boolean canPullTop = true, canPullBottom = true;

    /** 判断是否在动画 */
    private boolean isAnimation;

    /** 记录动画时,上一次的值 */
    private int lastAnimationValue;

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

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

    /**
     * 初始化
     */
    private void init() {
        headerView = initHeaderView();
        footerView = initFooterView();
        if (headerView != null) {
            // 获取headerView的高度
            headerView.measure(0, 0);
            headerHeight = headerView.getMeasuredHeight();
            post(new Runnable() {
                @Override
                public void run() {
                    // 设置headerView的高度
                    setViewHeight(headerView, 0);
                }
            });
            addHeaderView(headerView);
        } else {
            canPullTop = false;
        }
        if (footerView != null) {
            // 获取footerView的高度
            footerView.measure(0, 0);
            footerHeight = footerView.getMeasuredHeight();
            post(new Runnable() {
                @Override
                public void run() {
                    // 设置footerView的高度
                    setViewHeight(footerView, 0);
                }
            });
            addFooterView(footerView);
        } else {
            canPullBottom = false;
        }
        setOnScrollListener(this);
    }

    /**
     * 设置View的高度
     * Bug1 : 当height=0时,view会恢复原来的高度
     * Bug1解决办法 : 如果height=0时,隐藏view,并设为1
     * Bug2 : 当height=0时,分割线还会显示
     * Bug2解决办法 : 如果height=0时,隐藏分割线,还好ListView有设置是否显示分割线的方法
     * @param view
     * @param height
     */
    private void setViewHeight(View view, int height) {
        int visibility = height == 0 ? View.GONE : View.VISIBLE;
        view.setVisibility(visibility);
        boolean isShowDivider = height != 0;
        if (view == headerView) {
            setHeaderDividersEnabled(isShowDivider);
        } else {
            setFooterDividersEnabled(isShowDivider);
        }
        LayoutParams params = (LayoutParams) view.getLayoutParams();
        if (params == null) {
            params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
        height = height == 0 ? 1 : height;
        params.height = height;
        view.setLayoutParams(params);
    }

    /**
     * 初始化Header,子类实现
     * @return
     */
    protected abstract View initHeaderView();

    /**
     * 初始化Footer,子类实现
     * @return
     */
    protected abstract View initFooterView();

    /**
     * 重写onTouchEvent实现上拉和下拉功能
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isAnimation) {
            return super.onTouchEvent(event);
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN :
                currentY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE :
                // 单次移动的距离:手指移动的距离乘以移动速率,为了不让刷新的View显示太快
                float moveY = (event.getY() - currentY) * MOVE_RATIO;
                currentY = event.getY();
                // 上拉
                if (isTop && canPullTop) {
                    // 在最顶,如果上滑,不拦截
                    if (state == STATE_NO_REFRESH && moveY < 0) return super.onTouchEvent(event);
                    // 设置headerView高度
                    updateHeader((int) moveY);
                    // 如果不处理的话,下拉至刷新状态之后,不放手,然后上滑,发现设置不了headerView的高度
                    // 因为此时的ListView也会处理ACTION_MOVE事件下滑,所以我们要返回true,表示我们要处理事件
                    return true;
                }
                // 下拉
                if (isBottom && canPullBottom) {
                    // 在最底,如果下滑,不拦截
                    if (state == STATE_NO_REFRESH && moveY > 0) return super.onTouchEvent(event);
                    // 设置footerView高度
                    updateFooter((int) -moveY);
                    // 因为我们处理事件,所以ListView的滑动就停止了
                    // 所以,虽然我们设置了footerView的高度,但是还是显示不出来
                    // 因此,需要我们手动设置滑动
                    scrollBy(0, (int) -moveY);
                    // 表示处理事件
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP :
                currentY = 0;
                if (state == STATE_RELEASE_REFRESH) {
                    if (headerView.getHeight() >= headerHeight) {
                        // 上拉刷新
                        releaseRefresh();
                        state = STATE_REFRESHING;
                        onRefreshing(true);
                    } else if (footerView.getHeight() >= footerHeight) {
                        // 下拉加载
                        releaseRefresh();
                        state = STATE_REFRESHING;
                        onRefreshing(false);
                    } else {
                        // 没有刷新的话,回到原始状态
                        completeRefresh();
                    }
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 更新headerView
     * @param moveY
     */
    private void updateHeader(int moveY) {
        state = STATE_RELEASE_REFRESH;
        int height = headerView.getHeight() + moveY;
        height = Math.max(0, Math.min(REFRESH_VIEW_MAX_HEIGHT, height));
        setViewHeight(headerView, height);
        onProgress(true, height, headerHeight);
    }

    /**
     * 更新footerView
     * @param moveY
     */
    private void updateFooter(int moveY) {
        state = STATE_RELEASE_REFRESH;
        int height = footerView.getHeight() + moveY;
        height = Math.max(0, Math.min(REFRESH_VIEW_MAX_HEIGHT, height));
        setViewHeight(footerView, height);
        onProgress(false, footerView.getHeight(), footerHeight);
    }

    /**
     * 松手刷新
     */
    private void releaseRefresh() {
        final View refreshView = headerView.getHeight() > 1 ? headerView : footerView.getHeight() > 1 ? footerView : null;
        int refreshViewHeight = headerView.getHeight() > 1 ? headerHeight : footerView.getHeight() > 1 ? footerHeight : 0;
        if (refreshView == null) {
            return;
        }
        if (refreshViewHeight == 0) {
            return;
        }
        if (refreshView.getHeight() < 2) {
            return;
        }
        if (refreshView.getHeight() > refreshViewHeight) {
            lastAnimationValue = refreshView.getHeight();
            ValueAnimator animator = ValueAnimator.ofInt(refreshView.getHeight(), refreshViewHeight);
            animator.setDuration((refreshView.getHeight() - refreshViewHeight) / ANIMATION_STEP + 1);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int height = (int) animation.getAnimatedValue();
                    setViewHeight(refreshView, height);
                    // 因为上拉加载时,我们手动滚动了ListView,现在要恢复滚动的位置
                    if (isBottom) {
                        scrollBy(0, height - lastAnimationValue);
                        lastAnimationValue = height;
                    }
                }
            });
            animator.start();
        }
    }

    /**
     * 完成刷新
     * Bug1 : 手指正在滑动,打算取消刷新,而此时,外部调用了completeRefresh完成刷新,把refreshView重置了,
     *        造成手指还在滑动,refreshView突然不见
     * Bug1解决办法 : 根据 currentY != 0 判断手指是否在滑动,如果手指还在滑动,则不做任何操作,否则重置refreshView
     * 注意1 : 不能用 headerView.getHeight() != 0 这种方式判断是否显示有headerView,具体原因请看setViewHeight方法
     *         要用 headerView.getHeight() > 1 方式判断
     */
    public void completeRefresh() {
        if (currentY != 0) return;
        isAnimation = true;
        final View refreshView = headerView.getHeight() > 1 ? headerView : footerView.getHeight() > 1 ? footerView : null;
        if (refreshView == null) {
            isAnimation = false;
            return;
        }
        if (refreshView.getHeight() < 2) {
            isAnimation = false;
            return;
        }
        lastAnimationValue = refreshView.getHeight();
        ValueAnimator animator = ValueAnimator.ofInt(refreshView.getHeight(), 0);
        animator.setDuration(refreshView.getHeight() / ANIMATION_STEP + 1);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int height = (int) animation.getAnimatedValue();
                setViewHeight(refreshView, height);
                // 因为上拉加载时,我们手动滚动了ListView,现在要恢复滚动的位置
                if (isBottom) {
                    scrollBy(0, height - lastAnimationValue);
                    lastAnimationValue = height;
                }
                if (height == 0) {
                    state = STATE_NO_REFRESH;
                    isAnimation = false;
                    onComplete(refreshView == headerView);
                }
            }
        });
        animator.start();
    }

    /**
     * 判断是否最顶或者最低或者都不是
     * @param view
     * @param firstVisibleItem
     * @param visibleItemCount
     * @param totalItemCount
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem == 0) {
            isTop = true;
            isBottom = false;
        } else if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
            isBottom = true;
            isTop = false;
        } else {
            isTop = false;
            isBottom = false;
        }
        if (onScrollListener != null) {
            onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (onScrollListener != null) {
            onScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    /**
     * 设置是否可以上拉
     * @param canPullTop
     */
    public void setCanPullTop(boolean canPullTop) {
        this.canPullTop = canPullTop;
    }

    /**
     * 设置是否可以下拉
     * @param canPullBottom
     */
    public void setCanPullBottom(boolean canPullBottom) {
        this.canPullBottom = canPullBottom;
    }

    /**
     * 是否在刷新
     * @return
     */
    public boolean isRefreshing() {
        return state == STATE_REFRESHING;
    }

    /**
     * 因为AbsRefreshListView已经调用了setOnScrollListener方法,不能在外面调用setOnScrollListener,否则会影响AbsRefreshListView
     * 请使用该方法,和setOnScrollListener的功能是一样的
     * @param onScrollListener
     */
    public void setScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    /**
     * 下拉上拉时调用
     * @param isTop
     * @param currentHeight
     * @param viewHeight
     */
    protected void onProgress(boolean isTop, int currentHeight, int viewHeight) {

    }

    /**
     * 刷新时调用,子类实现
     * @param isTop
     */
    protected abstract void onRefreshing(boolean isTop);

    /**
     * 刷新完成时调用
     * @param isTop
     */
    protected void onComplete(boolean isTop) {

    }

}

代码的注释已经很清楚说明了上拉刷新和下拉加载的原理,还有遇到的一些坑和解决办法,相信你看得懂。
下面是我实现的子类:

package com.johan.library.viewtoolkit.refreshlistview;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.TextView;

import com.johan.library.R;

/**
 * Created by johan on 2017/7/27.
 */

public class RefreshListView extends AbsRefreshListView {

    private ImageView headerIconView, footerIconView;
    private TextView headerContentView, footerContentView;

    private RefreshMessage refreshMessage;

    private ObjectAnimator rotateAnimator;

    private OnRefreshListener onRefreshListener = new OnRefreshListener() {
        @Override
        public void onPullRefreshing() {}
        @Override
        public void onLoadRefreshing() {}
    };

    public RefreshListView(Context context) {
        super(context);
        refreshMessage = new RefreshMessage();
        initView();
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 支持attr自定义属性
        refreshMessage = new RefreshMessage();
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RefreshListView);
        if (array.hasValue(R.styleable.RefreshListView_refresh_pull_icon)) {
            refreshMessage.pullIcon = array.getDrawable(R.styleable.RefreshListView_refresh_pull_icon);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_pull_tip)) {
            refreshMessage.pullTip = array.getString(R.styleable.RefreshListView_refresh_pull_tip);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_pull_release_refresh_tip)) {
            refreshMessage.pullReleaseRefreshTip = array.getString(R.styleable.RefreshListView_refresh_pull_release_refresh_tip);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_pull_refreshing_tip)) {
            refreshMessage.pullRefreshingTip = array.getString(R.styleable.RefreshListView_refresh_pull_refreshing_tip);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_load_icon)) {
            refreshMessage.loadIcon = array.getDrawable(R.styleable.RefreshListView_refresh_load_icon);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_load_tip)) {
            refreshMessage.loadTip = array.getString(R.styleable.RefreshListView_refresh_load_tip);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_load_release_refresh_tip)) {
            refreshMessage.loadReleaseRefreshTip = array.getString(R.styleable.RefreshListView_refresh_load_release_refresh_tip);
        }
        if (array.hasValue(R.styleable.RefreshListView_refresh_load_refreshing_tip)) {
            refreshMessage.loadRefreshingTip = array.getString(R.styleable.RefreshListView_refresh_load_refreshing_tip);
        }
        array.recycle();
        initView();
    }

    @Override
    protected View initHeaderView() {
        View headerView = LayoutInflater.from(getContext()).inflate(R.layout.header_default, null);
        headerIconView = (ImageView) headerView.findViewById(R.id.header_default_icon);
        headerContentView = (TextView) headerView.findViewById(R.id.header_default_content);
        return headerView;
    }

    @Override
    protected View initFooterView() {
        View footerView = LayoutInflater.from(getContext()).inflate(R.layout.footer_default, null);
        footerIconView = (ImageView) footerView.findViewById(R.id.footer_default_icon);
        footerContentView = (TextView) footerView.findViewById(R.id.footer_default_content);
        return footerView;
    }

    private void initView() {
        if (refreshMessage.pullIcon != null) {
            headerIconView.setImageDrawable(refreshMessage.pullIcon);
        }
        headerContentView.setText(refreshMessage.pullTip);
        if (refreshMessage.loadIcon != null) {
            footerIconView.setImageDrawable(refreshMessage.loadIcon);
        }
        footerContentView.setText(refreshMessage.loadTip);
    }

    @Override
    protected void onRefreshing(boolean isTop) {
        View refreshView = isTop ? headerIconView : footerIconView;
        startRotateAnimation(refreshView);
        if (isTop) {
            onRefreshListener.onPullRefreshing();
            headerContentView.setText(refreshMessage.pullRefreshingTip);
        } else {
            onRefreshListener.onLoadRefreshing();
            footerContentView.setText(refreshMessage.loadRefreshingTip);
        }
    }

    @Override
    protected void onProgress(boolean isTop, int currentHeight, int viewHeight) {
        if (isTop) {
            String headerContent = currentHeight >= viewHeight ? refreshMessage.pullReleaseRefreshTip : refreshMessage.pullTip;
            headerContentView.setText(headerContent);
            rotateIcon(headerIconView, 360 * (currentHeight % viewHeight) / viewHeight);
        } else {
            String footerContent = currentHeight >= viewHeight ? refreshMessage.loadReleaseRefreshTip : refreshMessage.loadTip;
            footerContentView.setText(footerContent);
            rotateIcon(footerIconView, 360 * (currentHeight % viewHeight) / viewHeight);
        }
        endRotateAnimation();
    }

    @Override
    protected void onComplete(boolean isTop) {
        endRotateAnimation();
    }

    private void startRotateAnimation(View view) {
        rotateAnimator = ObjectAnimator.ofFloat(view, "rotation", 0, 359.0f);
        rotateAnimator.setDuration(500);
        rotateAnimator.setRepeatCount(-1);
        rotateAnimator.setInterpolator(new LinearInterpolator());
        rotateAnimator.start();
    }

    private void endRotateAnimation() {
        if (rotateAnimator != null) {
            rotateAnimator.end();
            rotateAnimator = null;
        }
    }

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

    public interface OnRefreshListener {
        void onPullRefreshing();
        void onLoadRefreshing();
    }

    private void rotateIcon(ImageView imageView, int angle) {
        imageView.setPivotX(imageView.getWidth() / 2);
        imageView.setPivotY(imageView.getHeight() / 2);
        imageView.setRotation(angle);
    }

    public void setPullTip(String pullTip) {
        refreshMessage.pullTip = pullTip;
    }

    public void setPullReleaseRefreshTip(String pullReleaseRefreshTip) {
        refreshMessage.pullReleaseRefreshTip = pullReleaseRefreshTip;
    }

    public void setPullRefreshingTip(String pullRefreshingTip) {
        refreshMessage.pullRefreshingTip = pullRefreshingTip;
    }

    public void setPullIcon(int pullIcon) {
        headerIconView.setImageResource(pullIcon);
    }

    public void setLoadTip(String loadTip) {
        refreshMessage.loadTip = loadTip;
    }

    public void setLoadReleaseRefreshTip(String loadReleaseRefreshTip) {
        refreshMessage.loadReleaseRefreshTip = loadReleaseRefreshTip;
    }

    public void setLoadRefreshingTip(String loadRefreshingTip) {
        refreshMessage.loadRefreshingTip = loadRefreshingTip;
    }

    public void setLoadIcon(int loadIcon) {
        footerIconView.setImageResource(loadIcon);
    }

    class RefreshMessage {
        public String pullTip = "下拉刷新";
        public String pullReleaseRefreshTip = "松手刷新";
        public String pullRefreshingTip = "正在刷新";
        public Drawable pullIcon = null;
        public String loadTip = "上拉加载";
        public String loadReleaseRefreshTip = "松手加载";
        public String loadRefreshingTip = "正在加载";
        public Drawable loadIcon = null;
    }

}

只要模仿RefreshListView,继承AbsRefreshListView,就很轻松实现自己的上拉刷新下拉加载的ListView。

代码已经上传到我的github:https://github.com/JohanMan/viewtoolkit

因为RefreshListView是已经实现了上拉刷新下拉加载了,可以直接使用。

使用实例:

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:refresh_view="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.johan.viewtoolkit.MainActivity">

    <com.johan.library.viewtoolkit.refreshlistview.RefreshListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="1px"
        android:divider="#cccccc"
        refresh_view:refresh_pull_icon="@drawable/my_refresh_icon"
        refresh_view:refresh_pull_tip="下拉刷新哦"
        refresh_view:refresh_pull_release_refresh_tip="松手刷新哦"
        refresh_view:refresh_pull_refreshing_tip="正在刷新哈"
        />

</LinearLayout>

activity

final RefreshListView listView = (RefreshListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setLoadIcon(R.drawable.my_refresh_icon);
listView.setLoadTip("上拉加载哦");
listView.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
    @Override
    public void onPullRefreshing() {
        // 模拟延时3秒
        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                listView.completeRefresh();
            }
        }, 3000);
    }
    @Override
    public void onLoadRefreshing() {
        // 模拟延时3秒
        listView.postDelayed(new Runnable() {
            @Override
            public void run() {
                listView.completeRefresh();
            }
        }, 3000);
    }
});
Android scrollview中嵌套listview实现listview的下拉刷新上拉加载更多 在Android中 scrollview中嵌套listview实现listview的下拉刷新上拉加载更多 阅读详情

相关推荐

Android使用PullToRefresh完成ListView下拉刷新和左滑删除

吹在前面的话:ListView下刷新刷功能相信从事Android开发的猿友们并不陌生,包括现在Google亲儿子SwipeRefreshLayout实现效果在一些APP上也能看见(不过个人不喜欢官方的刷新效果)。本文就带领一些刚入门android的朋友或者一起爱分享的朋友来简单的实现ListView的下拉刷新和左滑删除效果。一、本文主要内容: 使用PullToRefresh完成ListView下拉、

一名互联网时代苟且偷生的 Android码农 4953

自定义RecyclerView实现下拉刷新和上拉加载

通过导入第三方库实现ListView的上拉加载和下拉刷新比较简单,今天我要讲的是自定义RecyclerView实现下拉刷新和上拉加载。首先,自定义下拉刷新上拉加载是通过给RecyclerView添加头部和尾部实现的。而问题是RecyclerView并没有addHeaderView(View v)和addFooterView(View v)方法。 第一步:自定义HeaderAndFo

xiaoyantan的博客 8697

Android RecyclerView实现下拉刷新和上拉加载更多功能

为了提供更好的用户体验,我们常常需要在RecyclerView中实现下拉刷新和上拉加载更多的功能。为了实现这个功能,我们需要自定义一个RecyclerView的OnScrollListener,并在滑动到底部时触发加载更多的操作。综上所述,通过自定义的OnScrollListener,我们可以很方便地实现RecyclerView的下拉刷新和上拉加载更多功能。方法中,我们可以处理加载更多的逻辑,比如请求下一页的数据。方法中,我们可以处理下拉刷新的逻辑,比如重新加载数据。在加载完成后,记得调用。

DevWizard的博客 929

android studio上拉加载,AndroidStudio的PullToRefreshListView简单使用

PullToRefreshListView的下载:https://github.com/chrisbanes/Android-PullToRefresh1.先将其中的library倒入到Module,然后与App进行关联2.代码,我并不是将PullToRefreshListView直接写到xml中,而是创建成对象。PullToRefreshListViewpullToRefreshListVie...

weixin_39856589的博客 377

Android——使用自定义OnScrollListener实现下拉刷新和上拉加载更多功能

在refreshData()方法中,可以执行下拉刷新的操作,例如发送网络请求刷新数据,并更新RecyclerView的适配器。在onScrolled()方法中,通过判断滑动位置和总的item数量,来触发加载更多的操作。在实际开发中,你还可以根据项目的需求进行一些优化和扩展,例如添加加载失败的处理、优化加载更多的触发条件等。接下来,我们来实现下拉刷新的功能。在onLoadMore()方法中,可以执行上拉加载更多的操作,例如发送网络请求获取更多数据,并更新RecyclerView的适配器。

PixelInk的博客 364

Android 史上最简单的自定义listview上拉刷新下拉加载

今天有人问我,android上拉刷新下拉加载怎么实现。我对他说网上有很多例子啊,他说,网上是有很多例子,但是很杂,对于小白不友好。所以我就专门写了一个史上最简单的上拉刷新下拉加载。整个工程就一个activity,一个自定义listview,还有三个布局文件。是不是很简单很简洁? 老规矩先上程序运行截图 默认界面 下拉刷新 上拉加载 开始正题 首先我们先看三个布局...

迟到的微笑 7417

android 上拉加载,下拉刷新功能,Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法...

RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈。先贴图上来看看:使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式:1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayou...

weixin_31681589的博客 713

实现Android ListView带阻尼效果的下拉上拉刷新功能

ListView作为Android开发中极为重要的组件,其刷新机制对提升用户体验至关重要。本章将对ListView的刷新机制进行深入介绍,从基础的刷新流程开始,逐步分析其背后的原理和常见的优化手段。自定义Header/Footer是提升用户界面个性化和用户体验的重要手段。在ListView或RecyclerView中,Header和Footer区域提供了展示额外信息的空间,比如广告、重要通知、引导页等。通过自定义实现,开发者可以将更多的业务逻辑与界面风格融入应用,以满足产品的特定需求。

weixin_42575109的博客 961

Android MVP设计框架模板 之 漂亮ListView上拉刷新下拉加载更多

Android MVP设计框架模板 之 漂亮ListView上拉刷新下拉加载更多

xufeifandj的专栏 4597

Android实战:QQ风格滑动删除+上拉刷新+下拉加载完整实现

实现滑动删除不仅依赖事件处理,还需合理的组件架构支撑。选择合适的基类、科学组织 itemView 布局、维护内部状态,是打造稳定高性能控件的关键步骤。虽然提供了基础功能,但许多产品追求个性化UI,如QQ的萌宠动画、微博的星座主题等。这就要求我们能完全替换默认刷新头,实现高度自由的视觉表达。通过定义统一的接口协议,隔离UI层与业务逻辑层:// 触发刷新请求// 请求加载下一页// 删除指定条目// 状态变更通知。

weixin_42499004的博客 773

Android高级ListView组件实现与应用

本文还有配套的精品资源,点击获取 简介:在Android开发中,ListView组件是展示大量数据的常用方式。但是,为了更好地满足用户的交互需求,开发者需要扩展ListView功能,如实现上拉刷新、下拉加载更多、滑动删除、长按编辑和点击监听等。 XListView-Android-master 是一个开源项目,它集成了这些高级功能,使得开发者能够更方便地在应用...

weixin_35045970的博客 1624

Android自定义控件二

ListView, 是在原有的ListView的基础上, 增加下拉刷新头, 和加载更多的尾.

wchg21131的博客 118

Android高效实现ListView上拉下拉刷新与加载更多

本文还有配套的精品资源,点击获取 简介:在Android开发中,ListView组件常用于展示数据列表。为了改善用户体验,开发者需要为ListView添加上拉下拉刷新和加载更多数据的功能。本课程将探讨实现这些功能的不同方法,包括使用第三方库如SwipeRefreshLayout或XListView,以及自定义实现滚动监听逻辑。课程将涉及自定义行为、性能优化和不同设备及And...

weixin_42298778的博客 1511

Android-----XlistView上拉刷新下拉加载更多

1.想要实现xlistview上拉刷新下拉加载更多必须导入xlistview文件,在file-new-ImportModule-选择路径导入-导入完成之后在xlistview下的build.gradle更改下buildToolsVersion为“25.0.1”,最下方的改为..appcompat-v7:25.0.1 2.需要同步xlistview 否则不能引用里面的方法,具体为点击file-

leehbhs的博客 783

乐鑫ESP32-C6产测全流程:RAM固件测试与阈值校准实战指南

Wi-Fi模组量产测试是保障IoT设备射频性能与硬件可靠性的关键环节,其核心在于基于RAM的无损固件加载、可配置的阈值判定机制及多工位协同控制。该流程依托串口协议与结构化配置(如JSON阈值文件、.bin测试固件),实现RF性能(fb_rssi、tx_power)、GPIO导通性、Flash ID识别与固件版本校验等原子化测试项的自动化执行。技术价值体现在避免Flash擦写风险、支持动态温补与在线学习校准,并无缝对接MES系统实现质量数据闭环。典型应用场景包括ESP32-C6/C3/S2等乐鑫全系Wi-Fi

weixin_35756690的博客 311

【UE 反射】反射的原理是什么?如何使用机制?

静态类型检查确保了类型的早期一致性和高效的代码执行,但减少了灵活性。动态类型检查提供了更大的灵活性,但增加了运行时的开销和潜在的错误。反射作为一种动态特性,无论是在静态类型语言还是动态类型语言中,都提供了强大的运行时类型操作能力,但也伴随着性能和安全性方面的权衡。通过理解这些联系,开发者可以在不同的编程语言和应用场景中选择适合的类型检查方式和反射机制,以实现所需的动态行为和灵活性,同时保证性能和安全性。

hhw_hhw的博客 6033

超简单的 listview的第三方控件PullToRefreshListView 上拉刷新,下拉加载更多,分页加载

超简单的 listview的第三方控件PullToRefreshListView 上拉刷新,下拉加载更多,分页加载

随意而然的专栏 5578

ai数字人系统部署

这段代码实现了基本的语音交互闭环,用户通过语音输入问题,数字人识别语音后进行自然语言理解并生成回复,再将回复转换为语音输出。但实际应用中的 AI 数字人系统更为复杂,如形象渲染需专业图形引擎(如 Unity、Unreal Engine),自然语言处理也会涉及更复杂的模型微调及多轮对话管理等。一个完整的 AI 数字人系统涵盖形象生成、自然语言处理、动作控制等多个复杂功能,下面为你提供一个 Python 示例,整合多个库来实现数字人基础功能,包括语音交互、简单的自然语言理解以及通过虚拟形象展示对话。

weixin_ggwwsscc的博客 369

Android RefreshListView上拉刷新下拉加载

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34161083的博客 138

半导体AMHS系统振动控制技术演进:从0.5G到超低振动的工艺革命

在半导体制造进入埃米级时代后,AMHS的振动控制能力将成为区分厂商技术等级的标尺。国内企业通过“弹性机械设计-混合主动控制-智能调度”的三重创新,正逐步突破0.2G阈值,支撑12英寸晶圆厂向3nm/2nm制程升级。未来,振动抑制不再仅是物流系统的性能参数,而是与光刻、刻蚀等核心工艺深度耦合的基础能力,这将重塑半导体设备产业的竞争格局。

m0_60674045的博客 1584

RK3588之内存带宽计算及达成率

本章来和大家介绍一下内存带宽的计算与达成率

文艺小少年的博客 1814

自定义ListView的上拉刷新和下拉加载

今天自己写了一个 自定义ListView实现下拉加载和上拉刷新,别的不说了直接上代码 package com.wd.tech.mview; import android.annotation.SuppressLint; import android.content.Context; import android.os.Handler; import android.os.Message; i...

weixin_41649874的博客 426

Agile Modbus使用和学习笔记

i < len;这里仅仅需要提供获取函数即可,离散输入是只读类型i++) {return 0;只读类型寄存器i++) {return 0;i < len;

Car12 168

Android——ListView上拉刷新与下拉刷新

本文记述ListView的上拉与下拉的刷新数据 首先需要自定义一个MyListView,继承于ListView public class MyListView extends ListView implements AbsListView.OnScrollListener { private View bottomview; //尾文件 private View headview; //头文件 private int totaItemCounts;//用于表示是下拉还是上拉

weixin_44288786的博客 895

银河麒麟桌面系统批量交付:封装自动化ISO镜像,百台装机效率提升10倍

麒麟桌面系统自动化ISO镜像定制摘要 本文详细介绍麒麟桌面版ISO安装镜像的定制方法,通过修改系统文件实现自动化安装部署。主要内容包括:1) 挂载原版ISO并提取文件;2) 使用unsquashfs解压系统文件进行定制化修改,包括添加系统标记配置;3) 配置ky-installer.cfg自动应答文件,设置安装参数如用户名、密码、分区方案等;4) 修改grub引导配置启用自动安装模式。该流程支持磁盘加密、自定义分区、自动登录等功能,生成的定制镜像可满足批量部署需求,显著提升系统安装效率。

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 1508

电磁阀颤振工作原理

颤振 颤振,弹性结构在均匀气(或液)流中受到空气(或液体)动力、弹性力和惯性力(见达朗伯原理)的耦合作用而发生的大幅度振动,它是气动弹性力学中最重要的问题之一。 随着机电液一体化技术的发展,电液比例控制技术的应用越来越广泛。电液比例控制系统范围较广,在液压传动与控制过程中,系统能够接受模拟信号或数字信号,使输出的流量或压力连续成比例地变化,都属于此类。该控制系统的优点是充分利用了液压控制与电气控制的长处,即功率传递靠液压,信号处理靠电气的优化措施。 电液比例控制原理与要求 液压控制系统中两个最重要的被控参数

小幽余生不加糖 1万+
上一篇: Android 网络编程 - OkHttp源码解析
下一篇: Android 三方登录 - QQ登录
JohanMan
博客等级 码龄10年 72粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值