文/程序员男神
前言
本周被派遣到上海出差,去医院演示项目,收集收集医院的需求;大早上起得比平时早了那么一会,就感觉一天有点疲乏。晚上到了,突然精神来了,写篇文章压压惊。说实话,上拉刷新、下拉加载功能很常用,也很常见,但是需要自己开发时,却感觉那么多不知道选用哪个了?记录下来,让自己在刷新面前不在迷茫。
aj
概述
先上效果图:上拉刷新,下拉加载的功能。
废话不多说,先给出两个项目的地址:
下拉刷新
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
总结:
学习需要坚持。不要怂,就是干。
网友评论