Android自定义View实现竖向滑动回弹效果
作者:DwView 时间:2021-08-03 12:19:00
本文实例为大家分享了Android自定义View实现滑动回弹的具体代码,供大家参考,具体内容如下
前言
Android 页面滑动的时候的回弹效果
一、关键代码
public class UniversalBounceView extends FrameLayout implements IPull {
private static final String TAG = "UniversalBounceView";
//default.
private static final int SCROLL_DURATION = 200;
private static final float SCROLL_FRACTION = 0.4f;
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_ABSLISTVIEW = 1;
private static final int VIEW_TYPE_SCROLLVIEW = 2;
private static float VIEW_SCROLL_MAX = 720;
private int viewHeight;
private AbsListView alv;
private OnBounceStateListener onBounceStateListener;
private View child;
private Scroller scroller;
private boolean pullEnabled = true;
private boolean pullPaused;
private int touchSlop = 8;
private int mPointerId;
private float downY, lastDownY, tmpY;
private int lastPointerIndex;
private float moveDiffY;
private boolean isNotJustInClickMode;
private int moveDelta;
private int viewType = VIEW_TYPE_NORMAL;
public UniversalBounceView(Context context) {
super(context);
init(context);
}
public UniversalBounceView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public UniversalBounceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
scroller = new Scroller(context, new CustomDecInterpolator());
touchSlop = (int) (ViewConfiguration.get(context).getScaledTouchSlop() * 1.5);
}
class CustomDecInterpolator extends DecelerateInterpolator {
public CustomDecInterpolator() {
super();
}
public CustomDecInterpolator(float factor) {
super(factor);
}
public CustomDecInterpolator(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public float getInterpolation(float input) {
return (float) Math.pow(input, 6.0 / 12);
}
}
private void checkCld() {
int cnt = getChildCount();
if (1 <= cnt) {
child = getChildAt(0);
} else if (0 == cnt) {
pullEnabled = false;
child = new View(getContext());
} else {
throw new ArrayIndexOutOfBoundsException("child count can not be less than 0.");
}
}
@Override
protected void onFinishInflate() {
checkCld();
super.onFinishInflate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
viewHeight = h;
VIEW_SCROLL_MAX = h * 1 / 3;
}
private boolean isTouch = true;
public void setTouch(boolean isTouch) {
this.isTouch = isTouch;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!isTouch) {
return true;
} else {
try {
if (isPullEnable()) {
if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
if (Math.abs(ev.getY() - tmpY) < touchSlop) {
return super.dispatchTouchEvent(ev);
} else {
tmpY = Integer.MIN_VALUE;
}
}
return takeEvent(ev);
}
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
if (getVisibility() != View.VISIBLE) {
return true;
}
return super.dispatchTouchEvent(ev);
}
}
private boolean takeEvent(MotionEvent ev) {
int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
mPointerId = ev.getPointerId(0);
downY = ev.getY();
tmpY = downY;
scroller.setFinalY(scroller.getCurrY());
setScrollY(scroller.getCurrY());
scroller.abortAnimation();
pullPaused = true;
isNotJustInClickMode = false;
moveDelta = 0;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
pullPaused = false;
smoothScrollTo(0);
if (isNotJustInClickMode) {
ev.setAction(MotionEvent.ACTION_CANCEL);
}
postDelayed(new Runnable() {
@Override
public void run() {
if (getScrollY() == 0 && onBounceStateListener != null) {
onBounceStateListener.overBounce();
}
}
}, 200);
break;
case MotionEvent.ACTION_MOVE:
lastPointerIndex = ev.findPointerIndex(mPointerId);
lastDownY = ev.getY(lastPointerIndex);
moveDiffY = Math.round((lastDownY - downY) * getScrollFraction());
downY = lastDownY;
boolean canStart = isCanPullStart();
boolean canEnd = isCanPullEnd();
int scroll = getScrollY();
float total = scroll - moveDiffY;
if (canScrollInternal(scroll, canStart, canEnd)) {
handleInternal();
break;
}
if (Math.abs(scroll) > VIEW_SCROLL_MAX) {
return true;
}
if ((canStart && total < 0) || (canEnd && total > 0)) {
if (moveDelta < touchSlop) {
moveDelta += Math.abs(moveDiffY);
} else {
isNotJustInClickMode = true;
}
if (onBounceStateListener != null) {
onBounceStateListener.onBounce();
}
scrollBy(0, (int) -moveDiffY);
return true;
}
// else if ((total > 0 && canStart) || (total < 0 && canEnd)) {
// if (moveDelta < touchSlop) {
// moveDelta += Math.abs(moveDiffY);
// } else {
// isNotJustInClickMode = true;
// }
// scrollBy(0, -scroll);
// return true;
// }
break;
case MotionEvent.ACTION_POINTER_UP:
handlePointerUp(ev, 1);
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
private boolean canScrollInternal(int scroll, boolean canStart, boolean canEnd) {
boolean result = false;
if ((child instanceof RecyclerView) || (child instanceof AbsListView) || child instanceof ScrollView) {
viewType = VIEW_TYPE_ABSLISTVIEW;
result = canStart && canEnd;
} else if (child instanceof ScrollView || child instanceof NestedScrollView) {
viewType = VIEW_TYPE_SCROLLVIEW;
} else {
return false;
}
if (result) {
isNotJustInClickMode = true;
if (moveDelta < touchSlop) {
moveDelta += Math.abs(moveDiffY);
return true;
}
return false;
}
if (((scroll == 0 && canStart && moveDiffY < 0) || (scroll == 0 && canEnd && moveDiffY > 0) || (!canStart && !canEnd))) {
return true;
}
if (moveDelta < touchSlop) {
moveDelta += Math.abs(moveDiffY);
return true;
} else {
isNotJustInClickMode = true;
}
return false;
}
private void handleInternal() {
}
private void handlePointerUp(MotionEvent event, int type) {
int pointerIndexLeave = event.getActionIndex();
int pointerIdLeave = event.getPointerId(pointerIndexLeave);
if (mPointerId == pointerIdLeave) {
int reIndex = pointerIndexLeave == 0 ? 1 : 0;
mPointerId = event.getPointerId(reIndex);
// 调整触摸位置,防止出现跳动
downY = event.getY(reIndex);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
private void smoothScrollTo(int value) {
int scroll = getScrollY();
scroller.startScroll(0, scroll, 0, value - scroll, SCROLL_DURATION);
postInvalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (!pullPaused && scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
private float getScrollFraction() {
float ratio = Math.abs(getScrollY()) / VIEW_SCROLL_MAX;
ratio = ratio < 1 ? ratio : 1;
float fraction = (float) (-2 * Math.cos((ratio + 1) * Math.PI) / 5.0f) + 0.1f;
return fraction < 0.10f ? 0.10f : fraction;
}
@Override
public boolean isPullEnable() {
return pullEnabled;
}
@Override
public boolean isCanPullStart() {
if (child instanceof RecyclerView) {
RecyclerView recyclerView = (RecyclerView) child;
return !recyclerView.canScrollVertically(-1);
}
if (child instanceof AbsListView) {
AbsListView lv = (AbsListView) child;
return !lv.canScrollVertically(-1);
}
if (child instanceof RelativeLayout
|| child instanceof FrameLayout
|| child instanceof LinearLayout
|| child instanceof WebView
|| child instanceof View) {
return child.getScrollY() == 0;
}
return false;
}
@Override
public boolean isCanPullEnd() {
if (child instanceof RecyclerView) {
RecyclerView recyclerView = (RecyclerView) child;
return !recyclerView.canScrollVertically(1);
}
if (child instanceof AbsListView) {
AbsListView lv = (AbsListView) child;
int first = lv.getFirstVisiblePosition();
int last = lv.getLastVisiblePosition();
View view = lv.getChildAt(last - first);
if (null == view) {
return false;
} else {
return (lv.getCount() - 1 == last) &&
(view.getBottom() <= lv.getHeight());
}
}
if (child instanceof ScrollView) {
View v = ((ScrollView) child).getChildAt(0);
if (null == v) {
return true;
} else {
return child.getScrollY() >= v.getHeight() - child.getHeight();
}
}
if (child instanceof NestedScrollView) {
View v = ((NestedScrollView) child).getChildAt(0);
if (null == v) {
return true;
} else {
return child.getScrollY() >= v.getHeight() - child.getHeight();
}
}
if (child instanceof WebView) {
return (((WebView) child).getContentHeight() * ((WebView) child).getScale()) - (((WebView) child).getHeight() + ((WebView) child).getScrollY()) <= 10;
}
if (child instanceof RelativeLayout
|| child instanceof FrameLayout
|| child instanceof LinearLayout
|| child instanceof View) {
return (child.getScrollY() == 0);
}
return false;
}
/**
* 通过addView实现效果回弹效果
*
* @param replaceChildView 需要替换的View
*/
public void replaceAddChildView(View replaceChildView) {
if (replaceChildView != null) {
removeAllViews();
child = replaceChildView;
addView(replaceChildView);
}
}
public void setPullEnabled(boolean enable) {
pullEnabled = enable;
}
public interface OnBounceStateListener {
public void onBounce();
public void overBounce();
}
public void setOnBounceStateListener(OnBounceStateListener onBounceStateListener) {
this.onBounceStateListener = onBounceStateListener;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
try {
return super.dispatchKeyEvent(event);
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
return false;
}
@Override
public void dispatchWindowFocusChanged(boolean hasFocus) {
try {
super.dispatchWindowFocusChanged(hasFocus);
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
}
}
二、注意要点
滑动结束的时候要防止动画抖动
private void handlePointerUp(MotionEvent event, int type) {
int pointerIndexLeave = event.getActionIndex();
int pointerIdLeave = event.getPointerId(pointerIndexLeave);
if (mPointerId == pointerIdLeave) {
int reIndex = pointerIndexLeave == 0 ? 1 : 0;
mPointerId = event.getPointerId(reIndex);
// 调整触摸位置,防止出现跳动
downY = event.getY(reIndex);
}
}
来源:https://blog.csdn.net/DwView/article/details/108369481
标签:Android,View,滑动回弹
0
投稿
猜你喜欢
ArrayList和LinkedList的区别、扩容机制以及底层的实现方式
2023-11-27 01:26:57
Android仿支付宝上芝麻信用分雷达图
2023-04-21 18:44:30
Android实现拍照添加时间水印
2023-10-02 14:23:51
C#中winform控制textbox输入只能为数字的方法
2023-06-26 12:09:03
Springboot项目打war包docker包找不到resource下静态资源的解决方案
2022-01-01 07:03:55
java使用lambda表达式对List集合进行操作技巧(JDK1.8)
2021-05-30 06:41:06
C#利用ScriptControl动态执行JS和VBS脚本
2023-04-29 21:26:13
Java 继承与多态超详细梳理
2023-11-26 09:01:08
Java中ThreadLocal避免内存泄漏的方法详解
2023-04-02 12:51:42
详解如何在C#中接受或拒绝Excel中的修订
2023-06-19 21:08:29
MyBatis根据条件批量修改字段的方式
2023-12-16 03:24:01
Java 多层嵌套JSON类型数据全面解析
2022-07-18 05:13:05
C#实现简易计算器功能(附源码)
2021-07-18 00:16:00
详解Java8 CompletableFuture的并行处理用法
2023-07-27 11:48:06
MyBatis入门实例教程之创建一个简单的程序
2022-04-26 14:38:14
Flutter 快速实现聊天会话列表效果示例详解
2022-10-21 09:18:31
SpringBoot整合Elasticsearch并实现CRUD操作
2021-10-28 07:27:31
Android定时器Timer的停止和重启实现代码
2022-10-03 23:25:43
c# 类成员初始化顺序的特殊情况
2021-07-04 18:04:45
从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate
2023-05-31 20:37:20