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

废话不多说,先给出两个项目的地址:
下拉刷新
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





































苏公安网备 32011402010933号