package com.ypx.imagepicker.helper;
|
|
import android.animation.ValueAnimator;
|
|
import androidx.annotation.NonNull;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.RecyclerView;
|
|
import android.view.View;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
|
import com.ypx.imagepicker.utils.PViewSizeUtils;
|
import com.ypx.imagepicker.widget.TouchRecyclerView;
|
|
|
/**
|
* Description: 滑动辅助类
|
* <p>
|
* Author: peixing.yang
|
* Date: 2019/2/26
|
*/
|
public class RecyclerViewTouchHelper {
|
private TouchRecyclerView recyclerView;
|
private View topView;
|
private View maskView;
|
private boolean isScrollTopView = false;
|
private boolean isTopViewStick = false;
|
private int canScrollHeight;
|
private int stickHeight;
|
|
public static RecyclerViewTouchHelper create(TouchRecyclerView recyclerView) {
|
return new RecyclerViewTouchHelper(recyclerView);
|
}
|
|
private RecyclerViewTouchHelper(TouchRecyclerView recyclerView) {
|
this.recyclerView = recyclerView;
|
}
|
|
public RecyclerViewTouchHelper setTopView(View topView) {
|
this.topView = topView;
|
return this;
|
}
|
|
public RecyclerViewTouchHelper setMaskView(View maskView) {
|
this.maskView = maskView;
|
return this;
|
}
|
|
public RecyclerViewTouchHelper setCanScrollHeight(int canScrollHeight) {
|
this.canScrollHeight = canScrollHeight;
|
return this;
|
}
|
|
public RecyclerViewTouchHelper setStickHeight(int stickHeight) {
|
this.stickHeight = stickHeight;
|
return this;
|
}
|
|
private void setRecyclerViewPaddingTop(int top) {
|
recyclerView.setPadding(recyclerView.getPaddingStart(), top,
|
recyclerView.getPaddingEnd(), recyclerView.getPaddingBottom());
|
}
|
|
private int lastScrollY = 0;
|
|
public RecyclerViewTouchHelper build() {
|
setRecyclerViewPaddingTop(canScrollHeight + stickHeight);
|
recyclerView.post(new Runnable() {
|
@Override
|
public void run() {
|
setRecyclerViewPaddingTop(topView.getHeight());
|
}
|
});
|
recyclerView.setTouchView(topView);
|
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
@Override
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
super.onScrolled(recyclerView, dx, dy);
|
if (isImageGridCantScroll()) {
|
return;
|
}
|
int scrollY = getScrollYDistance();
|
if (isScrollTopView && topView.getTranslationY() != -canScrollHeight) {
|
if (lastScrollY == 0) {
|
lastScrollY = scrollY;
|
}
|
int distance = scrollY - lastScrollY;
|
if (distance >= canScrollHeight) {
|
setMaskAlpha(1);
|
topView.setTranslationY(-canScrollHeight);
|
setRecyclerViewPaddingTop(stickHeight);
|
} else {
|
if (distance <= 0) {
|
setMaskAlpha(0);
|
topView.setTranslationY(0);
|
} else {
|
float ratio = -distance * 1.00f / (-canScrollHeight * 1.00f);
|
setMaskAlpha(ratio);
|
topView.setTranslationY(-distance);
|
}
|
}
|
return;
|
}
|
if (isTopViewFullShow()) {
|
isTopViewStick = false;
|
setMaskAlpha(0);
|
}
|
|
if (isTopViewStick) {
|
int translate = -scrollY - topView.getHeight();
|
if (translate <= -canScrollHeight) {
|
topView.setTranslationY(-canScrollHeight);
|
setRecyclerViewPaddingTop(stickHeight);
|
isTopViewStick = false;
|
} else {
|
if (translate >= -20) {
|
translate = 0;
|
}
|
topView.setTranslationY(translate);
|
float ratio = topView.getTranslationY() * 1.00f / (-topView.getHeight() * 1.00f);
|
setMaskAlpha(ratio);
|
}
|
}
|
}
|
});
|
|
recyclerView.setDragScrollListener(new TouchRecyclerView.onDragScrollListener() {
|
@Override
|
public void onScrollOverTop(int distance) {
|
if (isImageGridCantScroll()) {
|
return;
|
}
|
isScrollTopView = true;
|
|
}
|
|
@Override
|
public void onScrollDown(int distance) {
|
if (isImageGridCantScroll()) {
|
return;
|
}
|
if (isRecyclerViewScrollToTop() && !isScrollTopView) {
|
setRecyclerViewPaddingTop(topView.getHeight());
|
isTopViewStick = true;
|
}
|
}
|
|
@Override
|
public void onScrollUp() {
|
lastScrollY = 0;
|
if (isImageGridCantScroll()) {
|
return;
|
}
|
if (isScrollTopView) {
|
transitTopWithAnim(!isRecyclerViewCanScrollOverScreen(), -1, true);
|
} else {
|
if (isTopViewStick && !isTopViewFullShow()) {
|
reset();
|
}
|
}
|
isScrollTopView = false;
|
}
|
});
|
return this;
|
}
|
|
private boolean isRecyclerViewScrollToTop() {
|
return !recyclerView.canScrollVertically(-1);
|
}
|
|
private boolean isRecyclerViewScrollToBottom() {
|
return !recyclerView.canScrollVertically(1);
|
}
|
|
private boolean isRecyclerViewCanScrollOverScreen() {
|
if (isImageGridCantScroll()) {
|
return false;
|
}
|
int count = 0;
|
if (recyclerView.getAdapter() != null) {
|
count = recyclerView.getAdapter().getItemCount();
|
}
|
int itemHeight = getItemHeight();
|
if (count < getSpanCount()) {
|
return false;
|
}
|
int lineCount = count % getSpanCount() == 0 ? count / getSpanCount() : count / getSpanCount() + 1;
|
return lineCount * itemHeight + recyclerView.getPaddingBottom() >
|
PViewSizeUtils.getScreenHeight(recyclerView.getContext()) - stickHeight;
|
}
|
|
private int spanCount = 0;
|
|
private int getSpanCount() {
|
if (spanCount != 0) {
|
return spanCount;
|
}
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
if (gridLayoutManager != null) {
|
spanCount = gridLayoutManager.getSpanCount();
|
return spanCount;
|
}
|
return 0;
|
}
|
|
/**
|
* 设置剪裁区域阴影
|
*
|
* @param ratio 阴影比例
|
*/
|
private void setMaskAlpha(float ratio) {
|
maskView.setVisibility(View.VISIBLE);
|
if (ratio <= 0) {
|
ratio = 0;
|
maskView.setVisibility(View.GONE);
|
} else if (ratio >= 1) {
|
ratio = 1;
|
}
|
maskView.setAlpha(ratio);
|
}
|
|
/**
|
* 剪裁区域+标题栏 是否完整显示
|
*/
|
private boolean isTopViewFullShow() {
|
return (topView.getTranslationY() == 0);
|
}
|
|
|
/**
|
* 选择图片recyclerView是否不可以滑动(数量少)
|
*/
|
private boolean isImageGridCantScroll() {
|
return !recyclerView.canScrollVertically(1) &&
|
!recyclerView.canScrollVertically(-1);
|
}
|
|
|
/**
|
* 获取recyclerView滑动距离
|
*/
|
private int getScrollYDistance() {
|
if (!(recyclerView.getLayoutManager() instanceof GridLayoutManager)) {
|
return 0;
|
}
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
int position = gridLayoutManager.findFirstVisibleItemPosition();
|
if (position < 0) {
|
position = 0;
|
}
|
View firstVisibleChildView = gridLayoutManager.findViewByPosition(position);
|
if (firstVisibleChildView == null) {
|
return 0;
|
}
|
|
int itemHeight = firstVisibleChildView.getHeight() + PViewSizeUtils.dp(recyclerView.getContext(), 2);
|
return (position / getSpanCount()) * itemHeight - firstVisibleChildView.getTop();
|
}
|
|
private int getItemHeight() {
|
if (!(recyclerView.getLayoutManager() instanceof GridLayoutManager)) {
|
return 0;
|
}
|
GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
int position = gridLayoutManager.findFirstVisibleItemPosition();
|
if (position < 0) {
|
position = 0;
|
}
|
View firstVisibleChildView = gridLayoutManager.findViewByPosition(position);
|
if (firstVisibleChildView == null) {
|
return 0;
|
}
|
return firstVisibleChildView.getHeight();
|
}
|
|
private void reset() {
|
final int scrollY = getScrollYDistance();
|
if (scrollY == 0) {
|
return;
|
}
|
ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f);
|
anim.setDuration(500);
|
anim.setInterpolator(new AccelerateDecelerateInterpolator());
|
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
@Override
|
public void onAnimationUpdate(ValueAnimator animation) {
|
float ratio = (Float) animation.getAnimatedValue();
|
recyclerView.scrollBy(0, (int) (scrollY * ratio));
|
}
|
});
|
anim.start();
|
}
|
|
/**
|
* 动画控制是否展开还是完整显示topView
|
*
|
* @param isFocusShow 是否强制完全展示
|
* @param scrollToPosition 滑动到制定的位置
|
*/
|
public void transitTopWithAnim(boolean isFocusShow, final int scrollToPosition, boolean isShowTransit) {
|
if (!isShowTransit) {
|
return;
|
}
|
if (isTopViewFullShow()) {
|
return;
|
}
|
final int startTop = (int) topView.getTranslationY();
|
//如果滑动区域小于标题栏高度的一半,则完全展示,否则收回剪裁区域到顶部
|
final int endTop = (isFocusShow || (startTop > -stickHeight / 2)) ? 0 : -canScrollHeight;
|
final int startPadding = recyclerView.getPaddingTop();
|
final float startAlpha = maskView.getAlpha();
|
ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f);
|
anim.setDuration(300);
|
anim.setInterpolator(new AccelerateDecelerateInterpolator());
|
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
@Override
|
public void onAnimationUpdate(ValueAnimator animation) {
|
float ratio = (Float) animation.getAnimatedValue();
|
int dis = (int) ((endTop - startTop) * ratio + startTop);
|
topView.setTranslationY(dis);
|
float maskAlpha = endTop == 0 ? (-startAlpha) * ratio + startAlpha : (1 - startAlpha) * ratio + startAlpha;
|
setMaskAlpha(maskAlpha);
|
int padding = (int) (((endTop == 0 ? topView.getHeight() : stickHeight) - startPadding) * ratio + startPadding);
|
setRecyclerViewPaddingTop(padding);
|
if (ratio == 1.0f) {
|
if (scrollToPosition == 0) {
|
recyclerView.scrollToPosition(0);
|
} else if (scrollToPosition != -1) {
|
recyclerView.smoothScrollToPosition(scrollToPosition);
|
}
|
}
|
}
|
});
|
anim.start();
|
}
|
|
public int dp(int dp) {
|
return PViewSizeUtils.dp(recyclerView.getContext(), dp);
|
}
|
}
|