文档开发者中心
我的

Android开发总结:上拉刷新、下拉加载库的使用

概述

先上效果图:上拉刷新,下拉加载的功能。

效果图

废话不多说,先给出两个项目的地址:
下拉刷新
https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
上拉刷新、上拉加载更多
https://github.com/captainbupt/android-Ultra-Pull-To-Refresh-With-Load-More/blob/master/README-cn.md


实现步骤

只讲使用方法,不扯淡,就是那么认真。

1、参数说明

  • 在旧版本中,ptr_duration_to_close是指回到刷新状态的事件。另外ptr_duration_to_close_header代表回到开始状态(不可见)的事件。这两个属性有点类似,因此可能造成误解,特别是在添加了footer之后。所以,在新版本中,ptr_duration_to_close被删除,而使用ptr_duration_to_back_refresh作为替代。同时,也加入了ptr_duration_to_back_header和ptr_duration_to_back_footer来区分头部和脚部。

  • 新版本中,添加ptr_duration_to_close_either代表回到开始状态(不可见)的事件。

  • 针对ptr_resistance,也分别添加ptr_resistance_header和ptr_resistance_footer。

2、添加的方法和类

  • setFooterView:对应于setHeaderView()。在set完footer后,你需要调用 addPtrUIHandler(),这和设置header的机制是一样的。

  • PtrHandler2:原有PtrHandler类的一个补充。当需要使用上拉加载更多的时候,你应该调用setPtrHandler(new PtrHandler2()),而不是setPtrHandler(new PtrHandler())。

  • PtrDefaultHandler2:实现了默认的 checkCanDoLoadMore() 逻辑,可以适用于大部分的View。机制和PtrDefaultHandler一致。

  • PtrClassicDefaultFooter:默认的footer,将默认header反转了过来。

  • setMode(Mode): Mode是本分支的一个新特性。通过调用setMode, 你可以任意的开启/关闭header或者footer。参数类型是一个枚举变量,可以通过以下方式调用:setMode(Mode.BOTH)。

3、使用方法

首先,在gradle中:

compile 'in.srain.cube:ptr-load-more:1.0.6'

下面是布局文件,activity_main.xml:

<?xml version='1.0' encoding='utf-8'?><LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
xmlns:app='http://schemas.android.com/apk/res-auto'
android:layout_width='match_parent'
android:layout_height='match_parent'
android:orientation='vertical'>

<in.srain.cube.views.ptr.PtrClassicFrameLayout
android:id='@+id/ptr_classic_frame_layout'
android:layout_width='match_parent'
android:layout_height='match_parent'
android:background='#FFFFFF'
app:ptr_duration_to_back_refresh='200'
app:ptr_duration_to_close_footer='1000'
app:ptr_duration_to_close_header='1000'
app:ptr_keep_header_when_refresh='true'
app:ptr_pull_to_fresh='false'
app:ptr_ratio_of_header_height_to_refresh='1.2'
app:ptr_resistance='1.7'>

<android.support.v7.widget.RecyclerView
android:id='@+id/recycle_view'
android:layout_width='match_parent'
android:layout_height='wrap_content' />
</in.srain.cube.views.ptr.PtrClassicFrameLayout></LinearLayout>

接下来就是我们的adapter的代码:

package com.winning.health.rims.recoverydemo.adapter;import android.app.Activity;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.util.DisplayMetrics;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import com.winning.health.rims.recoverydemo.R;import com.winning.health.rims.recoverydemo.model.Project;import com.winning.health.rims.recoverydemo.view.SlidingButtonView;import java.util.List;/**
* desc: adapter
* author:djj on 2017/7/3 12:49
* 简书:http://www.jianshu.com/u/dfbde65a03fc
*/public class RecycleAdapter extends RecyclerView.Adapter<RecycleAdapter.MyViewHolder> implements SlidingButtonView.IonSlidingButtonListener { private List<Project> data; private Context mContext; private SlidingButtonView mMenu = null; public RecycleAdapter(List<Project> data, Context mContext) { this.data = data; this.mContext = mContext;
} @Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view); return myViewHolder;
} @Override
public void onBindViewHolder(final MyViewHolder holder, int position) { //将数据填充到具体的view中
holder.itemTitle.setText(data.get(position).getItemTitle());
holder.itemDuration.setText(data.get(position).getItemDuration() + '');
holder.startTask.setText(data.get(position).getStartTask());
holder.tvDelete.setText(data.get(position).getTvDelete()); //设置内容布局的宽为屏幕宽度
DisplayMetrics metrics = new DisplayMetrics();
((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(metrics); int width = metrics.widthPixels;
holder.layoutContent.getLayoutParams().width = width; // 如果设置了回调,则设置点击事件
if (mIDeleteBtnClickListener != null) {
holder.startTask.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) { int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onStartItemClick(view, holder, n);
}
});
holder.layoutContent.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) { //判断侧滑是否打开
if (menuIsOpen()) {
closeMenu();
} else { int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onDeleteBtnClick(view, holder, n);
}
}
});
holder.tvDelete.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) { int n = holder.getLayoutPosition();
mIDeleteBtnClickListener.onDeleteBtnClick(view, holder, n);
}
});
}
} @Override
public int getItemCount() { if (data != null) { return data.size();
} return 0;
} /**
* 删除菜单打开信息接收
*/
@Override
public void onMenuIsOpen(View view) {
mMenu = (SlidingButtonView) view;
} /**
* 滑动或者点击了Item监听
*
* @param slidingButtonView
*/
@Override
public void onDownOrMove(SlidingButtonView slidingButtonView) { if (menuIsOpen()) { if (mMenu != slidingButtonView) {
closeMenu();
}
}
} /**
* 关闭菜单
*/
public void closeMenu() {
mMenu.closeMenu();
mMenu = null;
} /**
* 判断是否有菜单打开
*/
public Boolean menuIsOpen() { if (mMenu != null) { return true;
} return false;
} /**
* 定义接口回调
*/
private IonSlidingViewClickListener mIDeleteBtnClickListener; public void setOnSlidingViewClickListener(IonSlidingViewClickListener listener) { this.mIDeleteBtnClickListener = listener;
} public interface IonSlidingViewClickListener { void onStartItemClick(View view, MyViewHolder holder, int position); void onDeleteBtnClick(View view, MyViewHolder holder, int position);
} public class MyViewHolder extends RecyclerView.ViewHolder { public TextView itemTitle; public TextView itemDuration; public TextView startTask; public TextView tvDelete; public ViewGroup layoutContent; public MyViewHolder(View itemView) { super(itemView);
itemTitle = itemView.findViewById(R.id.item_title);
itemDuration = itemView.findViewById(R.id.item_duration);
startTask = itemView.findViewById(R.id.start_task);
tvDelete = itemView.findViewById(R.id.tv_delete);
layoutContent = itemView.findViewById(R.id.layout_content);
((SlidingButtonView) itemView).setSlidingButtonListener(RecycleAdapter.this);

}
}
}

接下来贴出item的xml代码item_list.xml:

<?xml version='1.0' encoding='utf-8'?><com.winning.health.rims.recoverydemo.view.SlidingButtonView xmlns:android='http://schemas.android.com/apk/res/android'
android:layout_width='match_parent'
android:layout_height='70dp'
android:background='@android:color/white'>

<RelativeLayout
android:layout_width='match_parent'
android:layout_height='match_parent'>

<RelativeLayout
android:id='@+id/layout_content'
android:layout_width='match_parent'
android:layout_height='match_parent'>

<LinearLayout
android:layout_width='match_parent'
android:layout_height='wrap_content'
android:layout_marginBottom='12dp'
android:layout_marginTop='12dp'
android:background='@color/white'
android:descendantFocusability='blocksDescendants'
android:gravity='center_vertical'
android:orientation='horizontal'>

<LinearLayout
android:layout_width='0dp'
android:layout_height='wrap_content'
android:layout_weight='1'
android:gravity='center_vertical'
android:orientation='vertical'>

<LinearLayout
android:layout_width='match_parent'
android:layout_height='wrap_content'
android:gravity='center_vertical'
android:orientation='horizontal'>

<TextView
android:id='@+id/item_title'
android:layout_width='0dp'
android:layout_height='wrap_content'
android:layout_marginLeft='10dp'
android:layout_weight='1'
android:text='推拿'
android:textColor='@color/textColor'
android:textSize='14sp' />

<TextView
android:id='@+id/item_duration'
style='@style/itemTextStyle'
android:layout_width='0dp'
android:layout_marginLeft='30dp'
android:layout_weight='0.5'
android:text='00:45:00' />
</LinearLayout>

<LinearLayout
android:layout_width='match_parent'
android:layout_height='wrap_content'
android:gravity='center_vertical'
android:orientation='horizontal'>

<TextView
android:id='@+id/item_patient_name'
style='@style/itemTextStyle'
android:layout_width='0dp'
android:layout_marginLeft='10dp'
android:layout_weight='0.5'
android:text='李青' />


<TextView
android:id='@+id/item_patient_age'
style='@style/itemTextStyle'
android:layout_width='wrap_content'
android:text='26岁' />

<TextView
android:id='@+id/item_patient_sex'
style='@style/itemTextStyle'
android:layout_width='0dp'
android:layout_weight='0.5'
android:text='男' />

<TextView
android:id='@+id/item_patient_num'
style='@style/itemTextStyle'
android:layout_width='0dp'
android:layout_weight='0.5'
android:text='20170511' />
</LinearLayout>

</LinearLayout>

<TextView
android:id='@+id/start_task'
android:layout_width='45dp'
android:layout_height='45dp'
android:layout_marginLeft='10dp'
android:layout_marginRight='10dp'
android:background='@drawable/button_back'
android:focusable='true'
android:focusableInTouchMode='false'
android:gravity='center'
android:text='开始'
android:textSize='12sp' />
</LinearLayout>
</RelativeLayout>

<TextView
android:id='@+id/tv_delete'
android:layout_width='80dp'
android:layout_height='match_parent'
android:layout_toEndOf='@+id/layout_content'
android:layout_toRightOf='@+id/layout_content'
android:background='@color/red'
android:gravity='center'
android:text='取 消'
android:textColor='@color/white' />
</RelativeLayout></com.winning.health.rims.recoverydemo.view.SlidingButtonView>

实体类Project的代码,set、get方法自己添加:

package com.winning.health.rims.recoverydemo.model;/**
* desc: 治疗项目实体类
* <p>
* author:djj
* <p>
* date: 2017/7/3 13:12
* <p>
* blog:http://www.jianshu.com/u/dfbde65a03fc
*/public class Project {

private String itemTitle; private int itemDuration; private String startTask; private String tvDelete;
}

由于这是在有滑动删除的Recycleview代码基础上添加的刷新,所以也把滑动的自定义贴出来:

package com.winning.health.rims.recoverydemo.view;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.HorizontalScrollView;import android.widget.TextView;import com.winning.health.rims.recoverydemo.R;/**
* desc: 侧滑按钮自定义
* author: dj
* date: 2017/3/10 10:01
*/public class SlidingButtonView extends HorizontalScrollView { private TextView mTextView_Delete; private int mScrollWidth; private Boolean isOpen = false; private Boolean once = false; public SlidingButtonView(Context context) { this(context, null);
} public SlidingButtonView(Context context, AttributeSet attrs) { this(context, attrs, 0);
} public SlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.setOverScrollMode(OVER_SCROLL_NEVER);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!once) {
mTextView_Delete = (TextView) findViewById(R.id.tv_delete);
once = true;
}
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { this.scrollTo(0, 0); //获取水平滚动条可以滑动的范围,即右侧按钮的宽度
mScrollWidth = mTextView_Delete.getWidth();
}
} @Override
public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE:
mIonSlidingButtonListener.onDownOrMove(this); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL:
changeScrollx(); return true; default: break;
} return super.onTouchEvent(ev);
} /**
* 按滚动条被拖动距离判断关闭或打开菜单
*/
public void changeScrollx() { if (getScrollX() >= (mScrollWidth / 2)) { this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
} else { this.smoothScrollTo(0, 0);
isOpen = false;
}
} /**
* 打开菜单
*/
public void openMenu() { if (isOpen) { return;
} this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
} /**
* 关闭菜单
*/
public void closeMenu() { if (!isOpen) { return;
} this.smoothScrollTo(0, 0);
isOpen = false;
} private IonSlidingButtonListener mIonSlidingButtonListener; public void setSlidingButtonListener(IonSlidingButtonListener listener) { this.mIonSlidingButtonListener = listener;
} public interface IonSlidingButtonListener { void onMenuIsOpen(View view); void onDownOrMove(SlidingButtonView slidingButtonView);
}
}

最后就是我们的activity的代码,Mainactivity.java:

package com.winning.health.rims.recoverydemo.activity;import android.graphics.Color;import android.os.Bundle;import android.os.CountDownTimer;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.DividerItemDecoration;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.View;import android.widget.Toast;import com.winning.health.rims.recoverydemo.R;import com.winning.health.rims.recoverydemo.adapter.RecycleAdapter;import com.winning.health.rims.recoverydemo.model.Project;import java.util.ArrayList;import java.util.List;import in.srain.cube.views.ptr.PtrClassicDefaultFooter;import in.srain.cube.views.ptr.PtrClassicDefaultHeader;import in.srain.cube.views.ptr.PtrClassicFrameLayout;import in.srain.cube.views.ptr.PtrDefaultHandler2;import in.srain.cube.views.ptr.PtrFrameLayout;import in.srain.cube.views.ptr.PtrHandler2;import in.srain.cube.views.ptr.util.PtrLocalDisplay;public class MainActivity extends AppCompatActivity {    private RecyclerView mRecyclerView;    private RecycleAdapter mAdapter;    private RecyclerView.LayoutManager mLayoutManager;    private List<Project> projectList;    private PtrClassicFrameLayout ptrClassicLayout;    @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initDate();
initView();


} //初始化数据
private void initDate() {
projectList = new ArrayList<Project>(); for (int i = 0; i < 20; i++) {
Project project = new Project();
project.setItemTitle('微波治疗');
project.setItemDuration(10);
project.setStartTask('开始');
project.setTvDelete('取消');
projectList.add(project);
}
} //初始化控件
private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recycle_view);
ptrClassicLayout = (PtrClassicFrameLayout) findViewById(R.id.ptr_classic_frame_layout);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecycleAdapter(projectList, MainActivity.this);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnSlidingViewClickListener(new RecycleAdapter.IonSlidingViewClickListener() { @Override
public void onStartItemClick(View view, final RecycleAdapter.MyViewHolder holder, int position) { //判断状态调收费接口
CountDownTimer timer = new CountDownTimer((20 * 1000), 1000) { @Override
public void onTick(long l) {
holder.itemDuration.setText(l / 1000 + '');
holder.startTask.setBackgroundResource(R.drawable.button_load_back);
holder.startTask.setTextColor(Color.parseColor('#0BC0BA'));
holder.startTask.setText('进行');

} @Override
public void onFinish() {
holder.itemDuration.setText('00:00:00');
holder.startTask.setBackgroundResource(R.drawable.button_no_back);
holder.startTask.setText('完成');
}
};
timer.start();
} @Override
public void onDeleteBtnClick(View view, RecycleAdapter.MyViewHolder holder, int position) {
Toast.makeText(MainActivity.this, 'onDeleteBtnClick', Toast.LENGTH_SHORT).show();

}
}); /**
* 经典 风格的头部实现
*/
final PtrClassicDefaultHeader header = new PtrClassicDefaultHeader(this);
header.setPadding(0, PtrLocalDisplay.dp2px(15), 0, 0);

PtrClassicDefaultFooter footer = new PtrClassicDefaultFooter(this);
footer.setPadding(0, 0, 0, PtrLocalDisplay.dp2px(15));

ptrClassicLayout.setHeaderView(header);
ptrClassicLayout.addPtrUIHandler(header);

ptrClassicLayout.setFooterView(footer);
ptrClassicLayout.addPtrUIHandler(footer); //mPtrFrame.setKeepHeaderWhenRefresh(true);//刷新时保持头部的显示,默认为true
//mPtrFrame.disableWhenHorizontalMove(true);//如果是ViewPager,设置为true,会解决ViewPager滑动冲突问题。
ptrClassicLayout.setPtrHandler(new PtrHandler2() { @Override
public boolean checkCanDoLoadMore(PtrFrameLayout frame, View content, View footer) { // 默认实现,根据实际情况做改动
return PtrDefaultHandler2.checkContentCanBePulledUp(frame, content, footer);
} /**
* 加载更多的回调
* @param frame
*/
@Override
public void onLoadMoreBegin(PtrFrameLayout frame) {
frame.postDelayed(new Runnable() { @Override
public void run() {

ptrClassicLayout.refreshComplete();
}
}, 2000);
} @Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { // 默认实现,根据实际情况做改动
return PtrDefaultHandler2.checkContentCanBePulledDown(frame, content, header);
} /**
* 下拉刷新的回调
* @param frame
*/
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
frame.postDelayed(new Runnable() { @Override
public void run() {

ptrClassicLayout.refreshComplete();
}
}, 1000);
}
});

}
}

这些都是我真实项目的demo,里面不止上拉刷新、下拉加载的功能,在以前功能基础上添加到,把这分享出来和大家交流,希望可以更好的进步。
最后贴上源码地址:
https://github.com/hellodonj/PtrClassicDemo.git


点赞
收藏
回复
分享
举报
浏览1340 发布于2019-08-16 06:22未知归属地
全部评论
最多点赞
最新发布
最早发布
暂无评论
暂无评论
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

目录 收起
概述
2、添加的方法和类
了解社区公约,与您携手共创和谐专业的开发者社区。