Android 开发 上拉加载更多功能实现
实现思维
开始之前先废话几句,Android系统没有提供上拉加载的控件,只提供了下拉刷新的SwipeRefreshLayout控件。这个控件我们就不废话,无法实现上拉刷新的功能。现在我们说说上拉加载更多的功能实现
思维步骤:
- 首先我们需要自定义重写RecyclerView,这个是重点.原因是,如果不重写RecyclerView会出现ItemView的点击与滑动的时候有事件分发冲突的问题(滑动无法得到down的触摸事件,或者点击无法得到up的触摸事件),我们重写RecyclerView目的就是重新分发事件,将down和up事件不被拦截,拦截我们需要的move事件.
- 创建一个叫页尾的布局文件,它用来在列表的最后面显示使用
- 接着我们需要想办法在RecyclerView.Adapter的适配器里导入这个页尾布局。你的列表内容适配器的普通item该如何实现还是如何实现。
- 为了导入这个页尾布局,我们需要在导入的List长度+1,因为这个页尾布局是另外加入的,需要在getItemCount()这个重写方法里返回List长度上+1。
- 现在就需要判断什么时候滚动到了列表的最后,这个时候我们需要重写一个之前写RecyclerView.Adapter适配器一般不触及的一个重写方法public int getItemViewType(int position) 。重写它根据position位置返回普通item和页尾item的ViewType。
- 能判断什么时候滚动到最后面后,我们就需要在写RecyclerView.Adapter适配器一样在onCreateViewHolder方法里导入布局,这里我们可以根据ViewType判断应该导入普通item还是页尾item
- 然后就是处理点击逻辑或者处理刷新逻辑了。在部分在activity中实现
代码部分
重写RecyclerView:
public class UpLoadingRecyclerView extends RecyclerView {
private static final String TAG = "MyRecyclerView";
private boolean isScroll;
private float downY;
private int mHeightPixels;
private OnUpScrollListener mOnUpScrollListener; public UpLoadingRecyclerView(@NonNull Context context) {
super(context);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mHeightPixels = displayMetrics.heightPixels;
} public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mHeightPixels = displayMetrics.heightPixels;
} public UpLoadingRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mHeightPixels = displayMetrics.heightPixels;
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
} /**
* 重写拦截的目的是只拦截移动事件,不拦截其他触摸事件
* @param e
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
super.onInterceptTouchEvent(e);//一定要super.onInterceptTouchEvent(e);不要让RecyclerView的其他事件分发受到影响,否则会出现不滚动的问题
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
isScroll = false;//按下不拦截
downY = e.getY();
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(downY - e.getY()) >= ViewConfiguration.get(
getContext()).getScaledTouchSlop()) {//判断是否产生了最小滑动
isScroll = true;//移动了拦截触摸事件
} else {
isScroll = false;
}
break;
case MotionEvent.ACTION_UP:
isScroll = false;//松开不拦截
break;
} return isScroll;
} @Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (downY-e.getY() > mHeightPixels/3){//判断是不是从下往上滑动了屏幕的三分之一
if (mOnUpScrollListener!=null) {
mOnUpScrollListener.onScroll();
Log.e(TAG, "onTouchEvent:滚动了");
} }
break; }
return super.onTouchEvent(e);
} public void setOnUpScrollListener(OnUpScrollListener listener){
this.mOnUpScrollListener = listener; } public interface OnUpScrollListener{
void onScroll();
} }
页尾布局 pull_up_refresh.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/colorGray5"
android:layout_width="match_parent"
android:layout_height="30dp"> <ProgressBar
android:id="@+id/footer_progress"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginRight="10dp"
android:visibility="gone"
android:indeterminateDrawable="@anim/pull_up_ic"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/footer_text"
app:layout_constraintBottom_toBottomOf="@id/footer_text"
app:layout_constraintRight_toLeftOf="@id/footer_text"/> <TextView
android:id="@+id/footer_text"
android:text="@string/pull_up_load_more"
android:textColor="@color/fontBlack3"
android:textSize="@dimen/font_size_14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/footer_progress"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
ProgressBar是刷新加载时候的动画图片 TextView就是文本内容
适配器代码
public class TNoticeListAdapter extends RecyclerView.Adapter<TNoticeListAdapter.ViewHolder> {
private List<TNoticeListBase.Notice> mList;
private static final int ITEM_VIEW = 1;
private static final int FOOTER_VIEW = 2;
public static final int FOOTER_TIPS = 0;//提示上拉加载更多
public static final int FOOTER_ING = 1;//加载中
public static final int FOOTER_ERROR = 2;//网络异常
public static final int FOOTER_FINISH = 3;//没有更多内容
private int footerState = 0;
private View mFooterView;
private OnFooterClickListener mFooterClickListener;
private OnItemClickListener mItemClickListener;
private int mScrollPosition;
public TNoticeListAdapter(List<TNoticeListBase.Notice> list){
this.mList = list; }
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_VIEW){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.t_notice_item,parent,false);
final ViewHolder viewHolder = new ViewHolder(itemView);
itemView.setOnClickListener(new View.OnClickListener() { //传出item的点击
@Override
public void onClick(View v) {
if (mItemClickListener!=null){
int postion = viewHolder.getAdapterPosition();
mItemClickListener.onClick(mList.get(postion).code);
}
}
});
return viewHolder;
}else {
mFooterView = LayoutInflater.from(parent.getContext()).inflate(R.layout.pull_up_refresh,parent,false);
mFooterView.setOnClickListener(new View.OnClickListener() { //传出页尾View的点击
@Override
public void onClick(View v) {
if (getFooterState()==FOOTER_ERROR && mFooterClickListener!=null){ //页尾View只在网络异常的时候可以被点击
mFooterClickListener.onClick();
} }
});
return new ViewHolder(mFooterView); }
} @Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
setScrollPosition(position);
if (getItemViewType(position) == ITEM_VIEW) {
TNoticeListBase.Notice data = mList.get(position);
holder.itemTime.setText("今天 "+TimeUtil.getHour24(data.time)+":"+TimeUtil.getMinute(data.time));
holder.itemContent.setText("\t\t" + data.content);
return;
}
switch (footerState){
case FOOTER_TIPS:
holder.footerText.setText(R.string.pull_up_load_more);
holder.footerProgress.setVisibility(View.GONE);
break;
case FOOTER_ING:
holder.footerText.setText(R.string.loading_more_for_you);
holder.footerProgress.setVisibility(View.VISIBLE);
break;
case FOOTER_ERROR:
holder.footerText.setText(R.string.network_exception_click_reload);
holder.footerProgress.setVisibility(View.GONE);
break;
case FOOTER_FINISH:
holder.footerText.setText(R.string.Theres_nothing_more);
holder.footerProgress.setVisibility(View.GONE);
break;
default:
holder.footerText.setText(R.string.pull_up_load_more);
holder.footerProgress.setVisibility(View.GONE);
break;
}
} @Override
public int getItemViewType(int position) {
//根据itemView的位置返回View的类型是普通还是页尾
if (position == getItemCount()-1){
return FOOTER_VIEW;
}else {
return ITEM_VIEW;
}
} @Override
public int getItemCount() {
return mList.size()==0?0:mList.size()+1;
} /**
* 得到页尾状态
* @return
*/
public int getFooterState() {
return footerState;
} /**
* 设置页尾状态
* @param footerState
*/
public void setFooterState(int footerState) {
this.footerState = footerState;
notifyDataSetChanged();
} /**
* 设置页尾点击监听
* @param listener
*/
public void setFooterClickListener(OnFooterClickListener listener){
this.mFooterClickListener = listener; }
/**
* 设置item的点击监听
* @param listener
*/
public void setItemClickListener(OnItemClickListener listener){
this.mItemClickListener = listener;
} /**
* 添加数据方法
* @param list
*/
public void addData(List<TNoticeListBase.Notice> list){
this.mList = list; } /**
* 得到滚动位置
* @return
*/
public int getScrollPosition() {
return mScrollPosition;
} /**
* 设置滚动位置 用于判断当前RecyclerView有没有滚动到最后
* @param position
*/
private void setScrollPosition(int position) {
this.mScrollPosition = position;
} public class ViewHolder extends RecyclerView.ViewHolder {
TextView itemTime;
TextView itemContent;
TextView footerText;
ProgressBar footerProgress;
public ViewHolder(@NonNull View itemView) {
super(itemView);
if (mFooterView!=null && itemView == mFooterView){
footerProgress = (ProgressBar)itemView.findViewById(R.id.footer_progress);
footerText = (TextView)itemView.findViewById(R.id.footer_text);
}else {
itemTime = (TextView) itemView.findViewById(R.id.time);
itemContent = (TextView) itemView.findViewById(R.id.content);
}
}
} public interface OnFooterClickListener{
void onClick(); } public interface OnItemClickListener{
void onClick(String code); }
}
在activity里的实现
public class TNoticeListActivity extends BaseActivity {
private UpLoadingRecyclerView mRecyclerView;
private SwipeRefreshLayout mSwitchRefresh;
private TNoticeListAdapter mAdapter;
private LinearLayoutManager mLinearLayoutManager;
private List<TNoticeListBase.Notice> mNoticeList; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
initClick(); } @Override
public int getLayout() {
return R.layout.activity_t_notice;
} @Override
public void initView() {
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerview);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager); } /**
* 模拟数据
*/
public void initData(){
mNoticeList = new ArrayList<>();
for (int i=0;i<2;i++){
TNoticeListBase.Notice notice = new TNoticeListBase().new Notice();
notice.time = 1547604994000L+i;
notice.content = "这是一个测试用的通知内容这是一个测试用的通知内容这是一个测试用的通知内容"+i;
notice.code = "dsfa13c1z616v5"+i;
mNoticeList.add(notice);
}
mAdapter = new TNoticeListAdapter(mNoticeList);
mRecyclerView.setAdapter(mAdapter); } /**
* 添加加载模拟数据
*/
public void addData(){
for (int i=0;i<10;i++){
TNoticeListBase.Notice notice = new TNoticeListBase().new Notice();
notice.time = 1547604994000L+i;
notice.content = "这是二个测试用的通知内容"+i;
notice.code = "dsfa13c1z616v5"+i;
mNoticeList.add(notice);
}
mAdapter.addData(mNoticeList);
mAdapter.setFooterState(TNoticeListAdapter.FOOTER_TIPS); } public void initClick(){
mRecyclerView.setOnUpScrollListener(new UpLoadingRecyclerView.OnUpScrollListener() {
@Override
public void onScroll() {
if (mAdapter.getItemCount()-1 == mAdapter.getPosition() && mAdapter.getFooterState() == TNoticeListAdapter.FOOTER_TIPS){
mAdapter.setFooterState(TNoticeListAdapter.FOOTER_ING);
//加载中的逻辑
L.e("正在加载中");
new Thread(new Runnable() {//模拟数据加载
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
addData(); }
});
}
}).start(); }
}
});
mAdapter.setFooterClickListener(new TNoticeListAdapter.OnFooterClickListener() {
@Override
public void onClick() {
L.e("网络异常点击了"); }
});
mAdapter.setItemClickListener(new TNoticeListAdapter.OnItemClickListener() {
@Override
public void onClick(String code) {
L.e("item被点击了");
}
});
mSwitchRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Toast.makeText(TNoticeListActivity.this, "没有更多数据了", Toast.LENGTH_SHORT).show();
mSwitchRefresh.setRefreshing(false);//刷新事件结束
}
});
} }
判断数据长度不够,提示已经是最新数据的办法,此方法可以在Activity里创建:
private final int PAGE_SIZE = 20;
private void downRefresh(List<TNoticeListBase.Notice> noticeList) {
mAdapter.addData(noticeList);
if (noticeList.size() < PAGE_SIZE) { //如果从服务器上获取的数据长度小于发送过去的一页长度,说明服务器没有数据了.页尾就显示没有更多数据
mAdapter.setFooterState(TNoticeListAdapter.FOOTER_FINISH);
} else {
mAdapter.setFooterState(TNoticeListAdapter.FOOTER_TIPS);
}
}
Android 开发 上拉加载更多功能实现的更多相关文章
- android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)
Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...
- Android 上拉加载更多功能
前几天看了github上面的例子,参照它的实现,自己又稍微改了一点,往项目里面增加了一个上拉加载更多功能.具体的实现如下: 首先要重写ListView: import android.content. ...
- Flutter移动电商实战 --(20)首页上拉加载更多功能的制作
这节课学习一下上拉加载效果,其实现在上拉加载的插件有很多,但是还没有一个插件可以说完全一枝独秀,我也找了一个插件,这个插件的优点就是服务比较好,作者能及时回答大家的问题.我觉的选插件也是选人,人对了, ...
- Android5.0新特性:RecyclerView实现上拉加载更多
RecyclerView是Android5.0以后推出的新控件,相比于ListView可定制性更大,大有取代ListView之势.下面这篇博客主要来实现RecyclerView的上拉加载更多功能. 基 ...
- SwipeRefreshLayout详解和自定义上拉加载更多
个人主页 演示Demo下载 本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout添加上拉加载更多的功能. 介绍之前,先来看一下SwipeRef ...
- 实现上拉加载更多的SwipeRefreshLayout
转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...
- 自定义ListView下拉刷新上拉加载更多
自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...
- Android中自定义ListView实现上拉加载更多和下拉刷新
ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能.这里我就简单记录一下实现过程. 实现这个功能的方法不止一个,Gi ...
- android ListView下拉刷新 上拉加载更多
背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...
随机推荐
- jquery Ajax 实现图片上传的功能。
$('#image').on('change', function () { var url = ""; var form = new FormDa ...
- Python学习笔记,day1
Python学习第一天 一.变量 变量定义的规则: 变量名只能是 字母.数字或下划线的任意组合 变量名的第一个字符不能是数字 以下关键字不能声明为变量名['and', 'as', 'assert', ...
- git 入门教程
git 入门教程之协同开发 前面我们已经介绍过远程仓库的相关概念,不过那时并没有深入探讨,只是讲解了如何创建远程仓库以及推送最新工作成果到远程仓库,实际上远程仓库对于团队协同开发很重要,不仅仅是团队协 ...
- OO第二单元作业分析
前言 这一单元关于线程安全的作业结束了,在助教提供的接口的帮助以及老师提供的设计模型的指导下,这三次作业还是相对轻松地完成了,中间也没有出现什么bug,可能就是因为简单的逻辑不容易出错吧,可惜两次都由 ...
- Linux----------mysql基础
目录 一.数据库介绍 1.1 数据库的优点 1.2 数据库的基本功能 1.3数据库的类型 1.4 关系型数据的组成 1.5 关系型数据库的常用组件 1.6 SQL语句 1.7 mysql命令使用 1. ...
- day05-数据类型与操作
- webbrowser 里的js函数和C#的函数互相调用方式
1.c#程序里要添加 [System.Runtime.InteropServices.ComVisibleAttribute(true)] 和 webBrowser1.ObjectForScrip ...
- Python argparse用法
import argparse import sys parser = argparse.ArgumentParser(description='this is for test.') parser. ...
- VUE踩坑之路
一.常见报错 1.vue-cli-service 不是内部或外部命令,也不是可运行程序 解决方案: 用以下命令安装Vue CLI就好 npm install -g @vue/cli # OR yarn ...
- Unity 3D中不得不说的yield协程与消息传递
1. 协程 在Unity 3D中,我们刚开始写脚本的时候肯定会遇到类似下面这样的需求:每隔3秒发射一个烟花.怪物死亡后20秒再复活之类的.刚开始的时候喜欢把这些东西都塞到Update里面去,就像下面这 ...