手把手教你实现Android RecyclerView上拉加载功能

Android实现RecyclerView的下拉刷新和上拉加载更多 需求先上效果图, Material Design风格的下拉刷新和上拉加载更多。 源码地址(欢迎star) https://github.com/studychen/SeeNewsV2如果对于RecyclerView还不熟悉,参见这篇 Android Material Design学习之RecyclerView代替 ListView下拉刷新效果图上拉时候会有一个圆形动画,刷新加载数据。思路使用Goog 阅读详情

心灵鸡汤:知之者不如好之者,好之者不如乐之者。

摘要

一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码,不仅增加了项目的冗余程度,而且出现BUG的时候,你却发现很难去改,正因为这样,我就下定决心去了解如何来实现RecyclerView的上拉加载功能,相信大家和我有过同样的情况,但是我相信,只要你给自己几分钟看完这篇文章,你就会发现实现一个上拉加载是非常的简单。

什么是上拉加载

上拉加载和下拉刷新相对应,在Android API LEVEL 19(即4.4)之后,Google官方推出了SwipeRefreshLayout和RecyclerView的共同使用,为我们提供了更加便捷的列表下拉刷新功能,但是,并没有给我们提供上拉加载功能,但是在RecyclerView强大的可扩展之下,Github上面有了很多开源项目实现了上拉加载功能,即我们不会一次性将所有数据加载到列表中,当用户滑动到底部时,再向服务器请求数据,再填充数据到列表中,这样不仅可以有更好的人机交互,同时在减少了服务器的压力的同时也对客户端的性能有了更好的提升。本篇文章主要通过介绍实现以下简单的上拉加载功能,同学们可以在掌握了最基本的实现功能之后,再通过扩展和优化,甚至可以封装成比较通用的代码,开源到Github上面。

Demo

实现思路

一、XML的实现

布局很简单,只有一个SwipeRefreshLayout包裹了一个RecyclerView,相信用过RecyclerView的都很容易看懂。如下为activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

然后,我们RecyclerView的Item布局也是非常简单,只有一个TextView。如下为item.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:background="@android:color/holo_blue_dark"
        android:gravity="center"
        android:textSize="30sp"
        android:textColor="#ffffff"
        android:text="11"
        android:layout_marginBottom="1dp"/>

</LinearLayout>

看到我们效果图都知道,在我们上拉时,还有一个提示的条目,我定义为 footview.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tips"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="30dp"
        android:textSize="15sp"
        android:layout_marginBottom="1dp"/>

</LinearLayout>

二、初始化SwipeRefreshLayout

在准备好了布局文件之后,我们就把目光转到Activity中去,首先我们需要初始化SwipeRefreshLayout,初始化也是很简单,这里省去了findView操作,所以就只有设置转动的颜色,还有设置刷新的监听事件:

private void initRefreshLayout() {
    refreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light,
            android.R.color.holo_orange_light, android.R.color.holo_green_light);
    refreshLayout.setOnRefreshListener(this);
}

@Override
public void onRefresh() {
    // 设置可见
    refreshLayout.setRefreshing(true);
    // 重置adapter的数据源为空
    adapter.resetDatas();
    // 获取第第0条到第PAGE_COUNT(值为10)条的数据
    updateRecyclerView(0, PAGE_COUNT);
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            // 模拟网络加载时间,设置不可见
            refreshLayout.setRefreshing(false);
        }
    }, 1000);
}

三、定义RecyclerView的Adapter

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<String> datas; // 数据源
    private Context context;    // 上下文Context
    
    private int normalType = 0;     // 第一种ViewType,正常的item
    private int footType = 1;       // 第二种ViewType,底部的提示View
    
    private boolean hasMore = true;   // 变量,是否有更多数据
    private boolean fadeTips = false; // 变量,是否隐藏了底部的提示
    
    private Handler mHandler = new Handler(Looper.getMainLooper()); //获取主线程的Handler

    public MyAdapter(List<String> datas, Context context, boolean hasMore) {
        // 初始化变量
        this.datas = datas;
        this.context = context;
        this.hasMore = hasMore;
    }
    
    // 获取条目数量,之所以要加1是因为增加了一条footView
    @Override
    public int getItemCount() {
        return datas.size() + 1;
    }
    
    // 自定义方法,获取列表中数据源的最后一个位置,比getItemCount少1,因为不计上footView
    public int getRealLastPosition() {
        return datas.size();
    }


    // 根据条目位置返回ViewType,以供onCreateViewHolder方法内获取不同的Holder
    @Override
    public int getItemViewType(int position) {
        if (position == getItemCount() - 1) {
            return footType;
        } else {
            return normalType;
        }
    }
    
    // 正常item的ViewHolder,用以缓存findView操作
    class NormalHolder extends RecyclerView.ViewHolder {
        private TextView textView;

        public NormalHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv);
        }
    }

    // // 底部footView的ViewHolder,用以缓存findView操作
    class FootHolder extends RecyclerView.ViewHolder {
        private TextView tips;

        public FootHolder(View itemView) {
            super(itemView);
            tips = (TextView) itemView.findViewById(R.id.tips);
        }
    }
    
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 根据返回的ViewType,绑定不同的布局文件,这里只有两种
        if (viewType == normalType) {
            return new NormalHolder(LayoutInflater.from(context).inflate(R.layout.item, null));
        } else {
            return new FootHolder(LayoutInflater.from(context).inflate(R.layout.footview, null));
        }
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        // 如果是正常的imte,直接设置TextView的值
        if (holder instanceof NormalHolder) {
            ((NormalHolder) holder).textView.setText(datas.get(position));
        } else {
            // 之所以要设置可见,是因为我在没有更多数据时会隐藏了这个footView
            ((FootHolder) holder).tips.setVisibility(View.VISIBLE);
            // 只有获取数据为空时,hasMore为false,所以当我们拉到底部时基本都会首先显示“正在加载更多...”
            if (hasMore == true) {
                // 不隐藏footView提示
                fadeTips = false;
                if (datas.size() > 0) {
                    // 如果查询数据发现增加之后,就显示正在加载更多
                    ((FootHolder) holder).tips.setText("正在加载更多...");
                }
            } else {
                if (datas.size() > 0) {
                    // 如果查询数据发现并没有增加时,就显示没有更多数据了
                    ((FootHolder) holder).tips.setText("没有更多数据了");
                    
                    // 然后通过延时加载模拟网络请求的时间,在500ms后执行
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // 隐藏提示条
                            ((FootHolder) holder).tips.setVisibility(View.GONE);
                            // 将fadeTips设置true
                            fadeTips = true;
                            // hasMore设为true是为了让再次拉到底时,会先显示正在加载更多
                            hasMore = true;
                        }
                    }, 500);
                }
            }
        }
    }

    // 暴露接口,改变fadeTips的方法
    public boolean isFadeTips() {
        return fadeTips;
    }

    // 暴露接口,下拉刷新时,通过暴露方法将数据源置为空
    public void resetDatas() {
        datas = new ArrayList<>();
    }
    
    // 暴露接口,更新数据源,并修改hasMore的值,如果有增加数据,hasMore为true,否则为false
    public void updateList(List<String> newDatas, boolean hasMore) {
        // 在原有的数据之上增加新数据
        if (newDatas != null) {
            datas.addAll(newDatas);
        }
        this.hasMore = hasMore;
        notifyDataSetChanged();
    }

}

四、初始化RecyclerView

private void initRecyclerView() {
    // 初始化RecyclerView的Adapter
    // 第一个参数为数据,上拉加载的原理就是分页,所以我设置常量PAGE_COUNT=10,即每次加载10个数据
    // 第二个参数为Context
    // 第三个参数为hasMore,是否有新数据
    adapter = new MyAdapter(getDatas(0, PAGE_COUNT), this, getDatas(0, PAGE_COUNT).size() > 0 ? true : false);
    mLayoutManager = new GridLayoutManager(this, 1);
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.setAdapter(adapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    // 实现上拉加载重要步骤,设置滑动监听器,RecyclerView自带的ScrollListener
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            // 在newState为滑到底部时
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                // 如果没有隐藏footView,那么最后一个条目的位置就比我们的getItemCount少1,自己可以算一下
                if (adapter.isFadeTips() == false && lastVisibleItem + 1 == adapter.getItemCount()) {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // 然后调用updateRecyclerview方法更新RecyclerView
                            updateRecyclerView(adapter.getRealLastPosition(), adapter.getRealLastPosition() + PAGE_COUNT);
                        }
                    }, 500);
                }
                
                // 如果隐藏了提示条,我们又上拉加载时,那么最后一个条目就要比getItemCount要少2
                if (adapter.isFadeTips() == true && lastVisibleItem + 2 == adapter.getItemCount()) {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // 然后调用updateRecyclerview方法更新RecyclerView
                            updateRecyclerView(adapter.getRealLastPosition(), adapter.getRealLastPosition() + PAGE_COUNT);
                        }
                    }, 500);
                }
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            // 在滑动完成后,拿到最后一个可见的item的位置
            lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
        }
    });
}

// 上拉加载时调用的更新RecyclerView的方法
private void updateRecyclerView(int fromIndex, int toIndex) {
    // 获取从fromIndex到toIndex的数据
    List<String> newDatas = getDatas(fromIndex, toIndex);
    if (newDatas.size() > 0) {
        // 然后传给Adapter,并设置hasMore为true
        adapter.updateList(newDatas, true);
    } else {
        adapter.updateList(null, false);
    }
}

所以,Activity的完整代码如下:

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
    private SwipeRefreshLayout refreshLayout;
    private RecyclerView recyclerView;
    private List<String> list;

    private int lastVisibleItem = 0;
    private final int PAGE_COUNT = 10;
    private GridLayoutManager mLayoutManager;
    private MyAdapter adapter;
    private Handler mHandler = new Handler(Looper.getMainLooper());

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

        initData();
        findView();
        initRefreshLayout();
        initRecyclerView();
    }

    private void initData() {
        list = new ArrayList<>();
        for (int i = 1; i <= 40; i++) {
            list.add("条目" + i);
        }
    }


    private void findView() {
        refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refreshLayout);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

    }

    private void initRefreshLayout() {
        refreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light,
                android.R.color.holo_orange_light, android.R.color.holo_green_light);
        refreshLayout.setOnRefreshListener(this);
    }

    private void initRecyclerView() {
        adapter = new MyAdapter(getDatas(0, PAGE_COUNT), this, getDatas(0, PAGE_COUNT).size() > 0 ? true : false);
        mLayoutManager = new GridLayoutManager(this, 1);
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setAdapter(adapter);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    if (adapter.isFadeTips() == false && lastVisibleItem + 1 == adapter.getItemCount()) {
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                updateRecyclerView(adapter.getRealLastPosition(), adapter.getRealLastPosition() + PAGE_COUNT);
                            }
                        }, 500);
                    }

                    if (adapter.isFadeTips() == true && lastVisibleItem + 2 == adapter.getItemCount()) {
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                updateRecyclerView(adapter.getRealLastPosition(), adapter.getRealLastPosition() + PAGE_COUNT);
                            }
                        }, 500);
                    }
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
            }
        });
    }

    private List<String> getDatas(final int firstIndex, final int lastIndex) {
        List<String> resList = new ArrayList<>();
        for (int i = firstIndex; i < lastIndex; i++) {
            if (i < list.size()) {
                resList.add(list.get(i));
            }
        }
        return resList;
    }

    private void updateRecyclerView(int fromIndex, int toIndex) {
        List<String> newDatas = getDatas(fromIndex, toIndex);
        if (newDatas.size() > 0) {
            adapter.updateList(newDatas, true);
        } else {
            adapter.updateList(null, false);
        }
    }

    @Override
    public void onRefresh() {
        refreshLayout.setRefreshing(true);
        adapter.resetDatas();
        updateRecyclerView(0, PAGE_COUNT);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                refreshLayout.setRefreshing(false);
            }
        }, 1000);
    }
}

后话

以上代码我是考虑到了更多的边界条件,所以在代码上会稍微多了一点,但是也不影响观看。大家也可以通过改变数据源的数量和PAGE_COUNT等来测试,每个人在具体使用上都会有不同的要求,所以基本代码我摆了出来,众口难调,更多的细节需要大家来优化,例如footView可以设置一个动画条,下拉刷新用其他样式替换原生的样式等,我想,这些对于学习完这篇文章的你来说,都会是简单的问题了。

Demo下载

Github下载:PullToLoadData-RecyclerView

优快云资源:PullToLoadData-RecyclerView

标签:  RecyclerView
RecyclerView实现上拉加载更多的正确姿势 最近项目上的需求需要实现下拉刷新和上拉加载更多的功能,RecyclerView下拉刷新我相信安卓的同学都会做,无非是利用SwipeRefreshLayout,然后给swipeRefreshLayout设置刷新监听,非常简单,所以这里不作赘述。 上拉加载更多,除了必要的逻辑,还需要自己控制loadingView的显示与隐藏,ListView中有一个方法addFooterView()可以轻松实现,在需要显示loadingView的时候设置view的可见性即可。 而RecyclerView中没有相应的方法,因此还 阅读详情

相关推荐

自己实现RecyclerView的上拉加载功能

在某些场景下,我们可能需要实现上拉加载更多数据的功能,以便加载大量数据时能够进行分页加载,提升用户体验。方法中,我们首先展示加载更多的提示,可以通过Adapter来实现。通过监听RecyclerView的滚动事件,判断滚动位置并触发加载更多数据的逻辑,再配合适当的展示和隐藏加载提示,可以实现流畅的上拉加载效果。接下来,我们需要监听RecyclerView的滚动事件,以便判断是否触发上拉加载的条件。方法获取总的列表项数量。如果最后一个可见项的位置等于总的列表项数量减一且用户向上滚动,即满足了上拉加载的条件。

TiCrystal的博客 266

RecyclerView进阶使用(上拉加载)

RecyclerView进阶使用前文准备上拉加载原理(addOnScrollListener)addOnScrollListeneronScrollStateChanged(@NonNull RecyclerView recyclerView, int newState)onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)方法1:方法2方法3结尾 前文 正文不在这里,可以直接选择跳过 记得上次更新还是在上次 可能是有人点赞才发现好久没更

weixin_47311938的博客 2592

自定义RecyclerView实现下拉刷新与上拉加载更多功能实战

虽然默认提供了 Material Design 风格的刷新动画,但在品牌化应用或深色主题中,原生样式可能显得突兀。为此,系统提供了丰富的 API 支持颜色、大小、甚至自定义 ProgressDrawable 的修改。同时,添加文字提示能显著提升可用性,尤其是在弱网环境下。init();textView.setText("下拉刷新...");// 添加到布局@Override在现代移动应用中,列表数据的无限滚动已成为标准交互模式之一。

weixin_42527665的博客 1165

【Android】上拉加载更多,下拉刷新数据快速实现

页面数据刷新

每天不是在写bug就是在写bug的路上 1277

ArcGIS小白必看:5分钟搞定面要素转点要素+自动计算经纬度坐标(附常见报错解决方案)

本文为ArcGIS新手详细解析了面要素转点要素并自动计算经纬度坐标的完整流程。核心在于正确理解和使用坐标系,并掌握ArcToolbox中的“要素转点”等工具。文章深入剖析了常见报错(如坐标系未定义、字段计算失败)的根源,提供了从基础操作到Python脚本自动化的实战解决方案,确保数据转换的准确与高效。

weixin_29284885的博客 575

Linux与Windows双系统,安装完linux后无法通过grub进入Windows的解决方法

先装Windows 10,Windows可正常启动,再装Kali,Kali提示装grub后照装。然而装完后重启发现无Windows选项。

sakurashockwave的博客 6454

BrewUI:为Homebrew包管理器打造的可视化图形界面

包管理器是现代开发环境中不可或缺的基础设施,它帮助开发者高效地安装、升级和管理软件依赖。在macOS生态中,Homebrew是最流行的包管理工具,但传统命令行操作对新手有较高门槛。图形界面(GUI)将命令行背后的逻辑可视化,让用户通过按钮、进度条和状态标识完成复杂操作。BrewUI正是这样一款工具,它为Homebrew提供了搜索、安装、批量升级、依赖查看和清理等核心功能。通过封装底层命令,它将依赖关系以树形结构呈现,并支持安全的批量升级与缓存清理。无论是刚接触macOS的开发者,还是希望提升包维护效率的资深

weixin_33851604的博客 686

x265探索与研究(四):如何编码视频?

如何编码视频?           本文介绍x265中的两种编码视频方法,一是采用命令行的方法;二是GUI的方式。   1、命令行的方法   (1)、第一种方式生成*.265格式的视频文件           第一种方式可以生成*.265格式的视频文件,对应的命令为: x265 --input-res 352x288 --fps 30 hall_cif_352x288_300.y

成长Bar的博客专栏 3936

教你实现Android RecyclerView上拉加载功能

编程学习 2017-04-16 22:13 摘要 一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码,不仅增加了项目的冗余程度,而且出现BUG的时候,你却发现很难去改,正因为这样,我就下定决心去了解如何来实现RecyclerView的上拉加载功能

u011277123的博客 1272

altium room 布局_ALtium 16相同重复模块PCB布局,复制布局走线

自己做了实际工程以后发现PCB器件名字后缀太长了,所以文章最后写了一下后缀的调整方法。第一种方法:1、建立子原理图如下,文件名CANM 2、之后新建一个顶层文件,文件名TOP,之后design—create sheet symbol form sheet or hdl。选择CANM,并放置到TOP中,如下 3、由于我们要多次调用CANM所以使用repeat命令修改如下: 4、之后根据自己的定义,连...

weixin_39609573的博客 5752

微信小游戏CE修改实战:从入门到精通(附防作弊思路)

本文深入探讨微信小游戏的安全攻防实战,重点分析CE修改、反编译和重打包等安全风险。通过详细的内存修改案例和代码保护方案对比,提供从客户端到服务端的全方位防护策略,帮助开发者构建有效的防作弊体系。

m2n3b4v5c6的博客 1147

Android recyclerview上拉加载更多

记录一下视频列表(recyclerview)上拉显示一个“加载更多”的item,然后加载数据。 效果图: 实现思路: 1.写两个item布局,一个用来展示数据,一个用来展示加载中也就是滑到最下方的时候显示的“加载更多”。 2.在adapter中写两个viewholder,对应两个xml文件。 3.adapter中重写getItemViewType用来鉴别当前item是不是最后一个,如果是最后一个...

孙先森的博客 1万+

uniapp - H5网页打包运行报错Failed to load resource: net::ERR_FILE_NOT_FOUND(uniApp打包成H5部署到服务器,打包h5白屏并报错页面空白)

Failed to load resource: net::ERR_FILE_NOT_FOUND,uniapp 坑之打包之后白屏,uniapp打包h5,HBuilderX 打包H5后空白报错 Failed to load resource: net::ERR FILE NOT_FOUND,UNI-APP H5应用白屏(空白页)解决方案,白屏并报错Failed to load resource: net::ERR_FILE_NOT_FOUND,uniapp开发,打包成H5部署到服务器,uniapp打包之后首页

🏆 前端领域优质创作者 3579

Recyclerview 上拉加载更多

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录效果如下主要步骤封装前代码1.adapter中2.定义一个抽象类3.MainActivity中封装后代码1. LoadMoreWrapper2. Adapter3.Activity中 效果如下 主要步骤 实现上拉加载更多主要有3步 定义两个 item , 第一个是正常显示内容的 item ,第二个是显示正在加载视图的 item 。如果 Adapter 中(position + 1 == itemCount),则说明滑到了最下面

weixin_47885879的博客 4386

【ZYNQ】OV5640 图像采集与显示

OV5640 是豪威(OmniVision)半导体公司推出的高性能的 CMOS 数字图像传感器,支持最高 500 万像素图像采集,支持 RAW RGB、RGB565、YCbCr422 等图像格式输出,可应用于手机、平板等电子设备。本文主要介绍如何使用 Zynq 驱动 OV5640 摄像头进行图像采集,并通过 HDMI 实时显示。

洋洋Young的博客 1433

GEE APP:基于Sentinel-1 SAR数据的洪水检测与制图系统:利用GEE平台实现洪涝区域提取、面积统计及矢量栅格化导出

本文介绍了一个基于Google Earth Engine平台的Sentinel-1 SAR洪水监测应用,通过自动化流程实现洪水淹没区域的精准检测与分析。系统采用Refined Lee滤波降噪和Otsu自适应阈值算法,结合SRTM地形数据消除干扰,可自动计算淹没面积并生成可视化结果。该工具提供用户友好的交互界面,支持多格式数据导出,适用于灾害应急响应、环境监测和城市规划等领域,具有云平台处理、算法鲁棒性强和操作简便等技术优势。

此星光明博客 327

Android实现下拉刷新和上拉加载数据(网络请求数据)

在安卓开发中,我们会经常遇到上拉加载和下拉刷新的功能,通过网络请求拿到数据然后添加到控件上,之前自己也在网上搜索过一些文章,但基本上用处不大,要么是效果不一样,要么是贴的代码不完整,从而导致功能无法正常使用(很蛋疼),所以在摸索了一阵之后,完成了一个大致OK的demo,供大家参考。有问题还望大家指正出来,感激不尽。。 ......

小口口的博客 1万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

android 官方上拉,手把手教你实现RecyclerView的下拉刷新和上拉加载更多

纵观多数App,下拉刷新和上拉加载更多是很常见的功能,但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新,上拉加载更多还要自己做。基于RecyclerView简单封装了这两个操作,下拉刷新支持LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager;上拉加载更多只支持前两者。话不多说先上效果图 数据来自干货集...

weixin_35329035的博客 842

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉加载更多

一、概述 我们公司目前开发的所有Android APP都是遵循iOS风格设计的,这并不是一个好现象。我决定将Android 5.x控件引入最近开发的项目中,使用RecyclerView取代以往使用的ListView、GridView,使用SwipeRefreshLayout取代pull-to-refresh第三方库,打造更符合Material Design风格的APP。本篇博客介绍的就是如何使用Swi

Veer Han 5万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

上一篇: IO图详解
biglayout
博客等级 码龄10年 0粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值