参考:
一行代码让RecyclerView分页滚动https://www.jianshu.com/p/3fe949083029
https://github.com/zhuguohui/HorizontalPage
RecyclerView的滚动事件OnScrollListener研究
上关键代码:
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* 实现RecycleView分页滚动的工具类
* Created by zhuguohui on 2016/11/10.
*/
public class PagingScrollHelper {
RecyclerView mRecyclerView = null;
private MyOnScrollListener mOnScrollListener = new MyOnScrollListener();
private MyOnFlingListener mOnFlingListener = new MyOnFlingListener();
private int offsetY = 0;
private int offsetX = 0;
int startY = 0;
int startX = 0;
enum ORIENTATION {
HORIZONTAL, VERTICAL, NULL
}
private ORIENTATION mOrientation = ORIENTATION.HORIZONTAL;
public void setUpRecycleView(RecyclerView recycleView) {
if (recycleView == null) {
throw new IllegalArgumentException("recycleView must be not null");
}
mRecyclerView = recycleView;
//处理滑动
recycleView.setOnFlingListener(mOnFlingListener);
//设置滚动监听,记录滚动的状态,和总的偏移量
recycleView.addOnScrollListener(mOnScrollListener);//更改了原来的set,改为add 了
//记录滚动开始的位置
recycleView.setOnTouchListener(mOnTouchListener);
//获取滚动的方向
updateLayoutManger();
}
public void updateLayoutManger() {
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (layoutManager != null) {
if (layoutManager.canScrollVertically()) {
mOrientation = ORIENTATION.VERTICAL;
} else if (layoutManager.canScrollHorizontally()) {
mOrientation = ORIENTATION.HORIZONTAL;
} else {
mOrientation = ORIENTATION.NULL;
}
if (mAnimator != null) {
mAnimator.cancel();
}
startX = 0;
startY = 0;
offsetX = 0;
offsetY = 0;
}
}
/**
* 获取总共的页数
*/
public int getPageCount() {
if (mRecyclerView != null) {
if (mOrientation == ORIENTATION.NULL) {
return 0;
}
if (mOrientation == ORIENTATION.VERTICAL && mRecyclerView.computeVerticalScrollExtent() != 0) {
return mRecyclerView.computeVerticalScrollRange() / mRecyclerView.computeVerticalScrollExtent();
} else if (mRecyclerView.computeHorizontalScrollExtent() != 0) {
Log.i("zzz","rang="+mRecyclerView.computeHorizontalScrollRange()+" extent="+mRecyclerView.computeHorizontalScrollExtent());
return mRecyclerView.computeHorizontalScrollRange() / mRecyclerView.computeHorizontalScrollExtent();
}
}
return 0;
}
ValueAnimator mAnimator = null;//ValueAnimator 实现弹性滑动效果
public void scrollToPosition(int position) {
if (mAnimator == null) {
mOnFlingListener.onFling(0, 0);
}
if (mAnimator != null) {
int startPoint = mOrientation == ORIENTATION.VERTICAL ? offsetY : offsetX, endPoint = 0;
if (mOrientation == ORIENTATION.VERTICAL) {
endPoint = mRecyclerView.getHeight() * position;
} else {
endPoint = mRecyclerView.getWidth() * position;
}
if (startPoint != endPoint) {
mAnimator.setIntValues(startPoint, endPoint);
mAnimator.start();
}
}
}
//由于使用了 RecyclerView 的 OnFlingListener,所以 RecycleView 的版本必须要 recyclerview-v7:25.0.0 以上。
public class MyOnFlingListener extends RecyclerView.OnFlingListener {
@Override
public boolean onFling(int velocityX, int velocityY) {
if (mOrientation == ORIENTATION.NULL) {
return false;
}
//获取开始滚动时所在页面的index
int p = getStartPageIndex();
//记录滚动开始和结束的位置
int endPoint = 0;
int startPoint = 0;
//如果是垂直方向
if (mOrientation == ORIENTATION.VERTICAL) {
startPoint = offsetY;
if (velocityY < 0) {
p--;
} else if (velocityY > 0) {
p++;
}
//更具不同的速度判断需要滚动的方向
//注意,此处有一个技巧,就是当速度为0的时候就滚动会开始的页面,即实现页面复位
endPoint = p * mRecyclerView.getHeight();
} else {
startPoint = offsetX;
if (velocityX < 0) {
p--;
} else if (velocityX > 0) {
p++;
}
endPoint = p * mRecyclerView.getWidth();
}
if (endPoint < 0) {
endPoint = 0;
}
//使用动画处理滚动
//我们知道如果我们直接调用 scrollBy(int x, int y) 这个方法去滑动,
// 那么是没有缓慢滑动的效果,看着有点愣,所以这里我们通过 ValueAnimator 这个类来实现缓慢滑动的效果,这个就很简单了
if (mAnimator == null) {
mAnimator = new ValueAnimator().ofInt(startPoint, endPoint);
mAnimator.setDuration(300);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int nowPoint = (int) animation.getAnimatedValue();
if (mOrientation == ORIENTATION.VERTICAL) {
int dy = nowPoint - offsetY;
//这里通过RecyclerView的scrollBy方法实现滚动。
mRecyclerView.scrollBy(0, dy);
} else {
int dx = nowPoint - offsetX;
mRecyclerView.scrollBy(dx, 0);
}
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//回调监听
if (null != mOnPageChangeListener) {
mOnPageChangeListener.onPageChange(getPageIndex());
}
//修复双击item bug
mRecyclerView.stopScroll();
startY = offsetY;
startX = offsetX;
}
});
} else {
mAnimator.cancel();
mAnimator.setIntValues(startPoint, endPoint);
}
mAnimator.start();
return true;
/*当这个方法被调用并且返回 true 的时候系统就不处理滑动了,而是将滑动交给我们自己处理。所以我们可以监听这个方法,
当我们执行快速滑动的时候在这个方法里面计算要滑动的距离并执行 scrollBy(int x, int y) 实现滑动,
然后直接返回 true,表示滑动我们自己处理了,不需要系统处理*/
}
}
/**
* 这个监听类主要有两个方法一个是 onScrollStateChanged(RecyclerView recyclerView, int newState) 滚动状态发生变化调用,
* onScrolled(RecyclerView recyclerView, int dx, int dy) RecyclerView 发生滚动和滚动完成调用。
* 有了这两个监听,当我们进行缓慢滑动我们就可以在
* onScrollStateChanged(RecyclerView recyclerView, int newState) 中监听滑动如果结束并且超过一定距离去执行翻页,
* 而通过 onScrolled(RecyclerView recyclerView, int dx, int dy) 方法可以记录当前的滑动距离
*/
public class MyOnScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//newState==0表示滚动停止,此时需要处理回滚
if (newState == 0 && mOrientation != ORIENTATION.NULL) {
boolean move;
int vX = 0, vY = 0;
if (mOrientation == ORIENTATION.VERTICAL) {
int absY = Math.abs(offsetY - startY);
//如果滑动的距离超过屏幕的一半表示需要滑动到下一页
move = absY > recyclerView.getHeight() / 2;
vY = 0;
if (move) {
vY = offsetY - startY < 0 ? -1000 : 1000;
}
} else {
int absX = Math.abs(offsetX - startX);
move = absX > recyclerView.getWidth() / 2;
if (move) {
vX = offsetX - startX < 0 ? -1000 : 1000;
}
}
mOnFlingListener.onFling(vX, vY);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//滚动结束记录滚动的偏移量
offsetY += dy;
offsetX += dx;
}
}
//自定义的 规定 向上,或是向右为+
public void selectpageHandle(boolean up){
if(mOrientation == ORIENTATION.VERTICAL){
if(up){
mOnFlingListener.onFling(0, 1000);
return;
}
mOnFlingListener.onFling(0, -1000);
}else{
if(up){
mOnFlingListener.onFling(1000, 0);
return;
}
mOnFlingListener.onFling(-1000, 0);
}
}
private MyOnTouchListener mOnTouchListener = new MyOnTouchListener();
private boolean firstTouch = true;
public class MyOnTouchListener implements View.OnTouchListener {
/**
* 要实现翻页滑动首先我们要确定是向前翻页还是向后翻页,
* 这里通过记录开始翻页前当前的位置和滑动后的位置比较即可得知,
* 下面选择手指触摸按下时滑动的位置为当前开始滑动位置:
* @param v
* @param event
* @return
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
//手指按下的时候记录开始滚动的坐标
if (firstTouch) {
//第一次touch可能是ACTION_MOVE或ACTION_DOWN,所以使用这种方式判断
firstTouch = false;
startY = offsetY;
startX = offsetX;
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
firstTouch = true;
}
return false;
}
}
private int getPageIndex() {
int p = 0;
if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) {
return p;
}
if (mOrientation == ORIENTATION.VERTICAL) {
p = offsetY / mRecyclerView.getHeight();
} else {
p = offsetX / mRecyclerView.getWidth();
}
return p;
}
private int getStartPageIndex() {
int p = 0;
if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) {
//没有宽高无法处理
return p;
}
if (mOrientation == ORIENTATION.VERTICAL) {
p = startY / mRecyclerView.getHeight();
} else {
p = startX / mRecyclerView.getWidth();
}
return p;
}
onPageChangeListener mOnPageChangeListener;
public void setOnPageChangeListener(onPageChangeListener listener) {
mOnPageChangeListener = listener;
}
public interface onPageChangeListener {
void onPageChange(int index);
}
}
ImageAdapter class
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.jlx.facedemo.R;
import com.jlx.facedemo.util.faceDiffCallback;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhuguohui on 2016/11/8.
*/
public class MyImageAdapter extends RecyclerView.Adapter {
private static final String TAG = "MyImageAdapter";
private Context mContext;
public static List imagePaths = new ArrayList<>();
public static List imageNames = new ArrayList<>();
public MyImageAdapter(Context context){
Log.d(TAG," this is constructions function");
this.mContext = context;
initAndGetLocalData();
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
private void initAndGetLocalData(){
//从数据库加载图片出来... 这边是方便加载.固定从指定文件夹中加载png后缀的文件
for (int i = 0;i < 10;i++){
String filePath = ImagePath + "/ImageDir/" + i +".png";
Log.e(TAG,filePath);
imagePaths.add(filePath);
imageNames.add(String.valueOf(i));
}
}
@Override
public MyImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG,"onCreateViewHolder ");
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.customgridimage, parent, false);
return new MyImageViewHolder(view);
}
//外部储存卡 路径
private final String ImagePath = Environment.getExternalStorageDirectory().getPath();
private static List imageNewPath = new ArrayList<>();
private bDeleteCallBack deCallBack;
public void setDeleteBack(bDeleteCallBack callBack){
this.deCallBack = callBack;
}
@Override
public void onBindViewHolder(final MyImageViewHolder holder, final int position) {
Bitmap bm = BitmapFactory.decodeFile(imagePaths.get(position));
holder.tv_title.setImageBitmap(bm);
holder.tv_select.setVisibility(View.INVISIBLE);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(mContext,"点击了 "+ imageNames.get(position) + "图像",Toast.LENGTH_SHORT).show();
// 如果当前item 被选中, 那么 是否可以删掉
if(holder.tv_select.getVisibility() == View.VISIBLE){
Log.i(TAG,"可以 删除 it");
if(imageNewPath != null){
imageNewPath.addAll(imagePaths);
imagePaths.remove(position);//在list 中移除掉
if(deCallBack != null){
deCallBack.deleterData(imageNewPath,imagePaths);//回调给 调用adapter的 activity class 更新视图
}
}
}
}
});
//长按 选中
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//Toast.makeText(mContext,"长按了 --- "+ imageNames.get(position) + "图像",Toast.LENGTH_SHORT).show();
if(holder.tv_select.getVisibility() == View.VISIBLE){
holder.tv_select.setVisibility(View.INVISIBLE);
return true;
}
holder.tv_select.setVisibility(View.VISIBLE);
return true;
}
});
}
@Override
public int getItemCount() {
return imagePaths.size();
}
class MyImageViewHolder extends RecyclerView.ViewHolder {
ImageView tv_title;//图像
ImageView tv_select;//被选中时的图像控件(一张与图像背景一样的,右上角有被选择标识的图样)
public MyImageViewHolder(View itemView) {
super(itemView);
tv_title = itemView.findViewById(R.id.sub_view);
tv_select = itemView.findViewById(R.id.selector);
}
}
}
import java.util.List;
public interface bDeleteCallBack {
void deleterData(List newData,List oldData);
}