实现思维

  开始之前先废话几句,Android系统没有提供上拉加载的控件,只提供了下拉刷新的SwipeRefreshLayout控件。这个控件我们就不废话,无法实现上拉刷新的功能。现在我们说说上拉加载更多的功能实现

 思维步骤:

  1. 首先我们需要自定义重写RecyclerView,这个是重点.原因是,如果不重写RecyclerView会出现ItemView的点击与滑动的时候有事件分发冲突的问题(滑动无法得到down的触摸事件,或者点击无法得到up的触摸事件),我们重写RecyclerView目的就是重新分发事件,将down和up事件不被拦截,拦截我们需要的move事件.
  2. 创建一个叫页尾的布局文件,它用来在列表的最后面显示使用
  3. 接着我们需要想办法在RecyclerView.Adapter的适配器里导入这个页尾布局。你的列表内容适配器的普通item该如何实现还是如何实现。
  4. 为了导入这个页尾布局,我们需要在导入的List长度+1,因为这个页尾布局是另外加入的,需要在getItemCount()这个重写方法里返回List长度上+1。
  5. 现在就需要判断什么时候滚动到了列表的最后,这个时候我们需要重写一个之前写RecyclerView.Adapter适配器一般不触及的一个重写方法public int getItemViewType(int position) 。重写它根据position位置返回普通item和页尾item的ViewType。
  6. 能判断什么时候滚动到最后面后,我们就需要在写RecyclerView.Adapter适配器一样在onCreateViewHolder方法里导入布局,这里我们可以根据ViewType判断应该导入普通item还是页尾item
  7. 然后就是处理点击逻辑或者处理刷新逻辑了。在部分在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 开发 上拉加载更多功能实现的更多相关文章

  1. android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)

    Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...

  2. Android 上拉加载更多功能

    前几天看了github上面的例子,参照它的实现,自己又稍微改了一点,往项目里面增加了一个上拉加载更多功能.具体的实现如下: 首先要重写ListView: import android.content. ...

  3. Flutter移动电商实战 --(20)首页上拉加载更多功能的制作

    这节课学习一下上拉加载效果,其实现在上拉加载的插件有很多,但是还没有一个插件可以说完全一枝独秀,我也找了一个插件,这个插件的优点就是服务比较好,作者能及时回答大家的问题.我觉的选插件也是选人,人对了, ...

  4. Android5.0新特性:RecyclerView实现上拉加载更多

    RecyclerView是Android5.0以后推出的新控件,相比于ListView可定制性更大,大有取代ListView之势.下面这篇博客主要来实现RecyclerView的上拉加载更多功能. 基 ...

  5. SwipeRefreshLayout详解和自定义上拉加载更多

    个人主页 演示Demo下载 本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout添加上拉加载更多的功能. 介绍之前,先来看一下SwipeRef ...

  6. 实现上拉加载更多的SwipeRefreshLayout

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...

  7. 自定义ListView下拉刷新上拉加载更多

    自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...

  8. Android中自定义ListView实现上拉加载更多和下拉刷新

    ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能.这里我就简单记录一下实现过程. 实现这个功能的方法不止一个,Gi ...

  9. android ListView下拉刷新 上拉加载更多

    背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...

随机推荐

  1. pycharm+pydesigner+pyqt5 如何添加图片资源

    pydesigner 上添加资源比较容易: 步骤一用于编辑,步骤二步创建,步骤三创建文件新的qrc: 步骤一:新建一个Prefix,步骤二给prefix添加资源文件.至此,资源文件添加完成 采用 Py ...

  2. web爬虫,BeautifulSoup

    BeautifulSoup 该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后遍可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XML中查找指定元素变得简单. 1 2 3 ...

  3. 微信小程序wx.navigateTo页面不跳转

    排查后发现: 若是在全局app.json中配置了tabBar,引用的链接与wx.navigateTo页面跳转url地址相同就无法实现跳转.

  4. django中云存储静态文件

    Django自带文件存储系统存储在本地文件夹,如果我们将文件存储在云端,需要自定义文件存储系统. 自定义文件存储系统需要继承django.core.files.storage.Storage from ...

  5. ubutu16.04 安装Tenda u12无线网卡驱动

    ubutu16.04 安装Tenda u12无线网卡驱动 一些问题: 1) Tenda u12 linux版本的驱动支持 kernel 2.6 到 4.4,而前系统内版本核为4.10,所以编译不过去啦 ...

  6. 使用 Flask-Docs 自动生成 Api 文档

    影响我写文档的原因可能是代码和文档分离,有时候写完代码会忘记补文档,而且不能及时查看,使用 Flask-Docs 可以解决我的问题,这个插件可以根据代码注释生成文档页面,代码注释改动文档可以及时更新, ...

  7. 解决java新开页面被拦截的问题

    在开发中遇到from表单利用 target="_blank" 属性新开页面时被拦截. 用ajax让form表单提交,这时有可能浏览器会拦截新开页面,这时只 需要设置 ajax 同步 ...

  8. Spring Boot 全局异常处理

    Spring Boot版本 1.5 @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExcept ...

  9. DockerFile详解--转载

    COPY 复制文件 格式: COPY ... COPY ["",... ""] 和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用. CO ...

  10. 结对编程core_6

    林静雯PB16060913 李鑫PB16061107 对于这种结对的工作,由于有过电子设计实践的基础,大概知道建一个工程需要做的事,有点经验还是有帮助的. 一.问题要求: 1·主要功能是随机产生有效的 ...