Android自定義View實現(xiàn)豎向滑動回彈效果
更新時間:2022年04月18日 15:49:39 作者:DwView
這篇文章主要為大家詳細介紹了Android自定義View實現(xiàn)滑動回彈效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了Android自定義View實現(xiàn)滑動回彈的具體代碼,供大家參考,具體內容如下
前言
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); ? ? ? ? ? ? // 調整觸摸位置,防止出現(xiàn)跳動 ? ? ? ? ? ? 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實現(xiàn)效果回彈效果 ? ? ?* ? ? ?* @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); ? ? ? ? ? ? // 調整觸摸位置,防止出現(xiàn)跳動 ? ? ? ? ? ? downY = event.getY(reIndex); ? ? ? ? } ? ? }?
總結
以上就是文章的主要內容,實現(xiàn)了豎向滑動回彈的效果。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Flutter 狀態(tài)管理scoped model源碼解讀
這篇文章主要為大家介紹了Flutter 狀態(tài)管理scoped model源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11實時獲取股票數(shù)據(jù)的android app應用程序源碼分享
本文我們分享一個實時獲取股票數(shù)據(jù)的android app應用程序源碼分享,可以作為學習使用,本文貼出部分重要代碼,需要的朋友可以參考下本文2015-09-09android監(jiān)控sim卡有沒有服務示例(sim卡管理)
android監(jiān)聽SIM卡有沒有服務,可以使用android.telephony.PhoneStateListener類來實現(xiàn),下面是一個簡單的小例子,大家參考使用吧2014-01-01Android編程使用AlarmManager設置鬧鐘的方法
這篇文章主要介紹了Android編程使用AlarmManager設置鬧鐘的方法,結合具體實例分析了Android基于AlarmManager實現(xiàn)鬧鐘功能的設置、取消、顯示等相關操作技巧,需要的朋友可以參考下2017-03-03native.js獲取手機硬件基本信息實例代碼android版
本文為大家分享了native.js獲取手機硬件基本信息實例代碼android版包括手機MAC地址,手機內存大小,手機存儲空間大小,手機CPU信息等手機硬件基本信息2018-09-09