Android自定義View實(shí)現(xiàn)豎向滑動回彈效果
本文實(shí)例為大家分享了Android自定義View實(shí)現(xiàn)滑動回彈的具體代碼,供大家參考,具體內(nèi)容如下
前言
Android 頁面滑動的時(shí)候的回彈效果
一、關(guān)鍵代碼
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); ? ? ? ? ? ? // 調(diào)整觸摸位置,防止出現(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實(shí)現(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(); ? ? ? ? } ? ? } ? ? }
二、注意要點(diǎn)
滑動結(jié)束的時(shí)候要防止動畫抖動
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); ? ? ? ? ? ? // 調(diào)整觸摸位置,防止出現(xiàn)跳動 ? ? ? ? ? ? downY = event.getY(reIndex); ? ? ? ? } ? ? }?
總結(jié)
以上就是文章的主要內(nèi)容,實(shí)現(xiàn)了豎向滑動回彈的效果。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flutter 狀態(tài)管理scoped model源碼解讀
這篇文章主要為大家介紹了Flutter 狀態(tài)管理scoped model源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11實(shí)時(shí)獲取股票數(shù)據(jù)的android app應(yīng)用程序源碼分享
本文我們分享一個(gè)實(shí)時(shí)獲取股票數(shù)據(jù)的android app應(yīng)用程序源碼分享,可以作為學(xué)習(xí)使用,本文貼出部分重要代碼,需要的朋友可以參考下本文2015-09-09Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)雷達(dá)View效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06android監(jiān)控sim卡有沒有服務(wù)示例(sim卡管理)
android監(jiān)聽SIM卡有沒有服務(wù),可以使用android.telephony.PhoneStateListener類來實(shí)現(xiàn),下面是一個(gè)簡單的小例子,大家參考使用吧2014-01-01詳解如何實(shí)現(xiàn)一個(gè)Kotlin函數(shù)類型
這篇文章主要為大家詳細(xì)介紹了如何實(shí)現(xiàn)一個(gè)Kotlin函數(shù)類型,文中的實(shí)現(xiàn)方法講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-10-10Flutter實(shí)現(xiàn)App功能引導(dǎo)頁
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)App功能引導(dǎo)頁,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Android編程使用AlarmManager設(shè)置鬧鐘的方法
這篇文章主要介紹了Android編程使用AlarmManager設(shè)置鬧鐘的方法,結(jié)合具體實(shí)例分析了Android基于AlarmManager實(shí)現(xiàn)鬧鐘功能的設(shè)置、取消、顯示等相關(guān)操作技巧,需要的朋友可以參考下2017-03-03native.js獲取手機(jī)硬件基本信息實(shí)例代碼android版
本文為大家分享了native.js獲取手機(jī)硬件基本信息實(shí)例代碼android版包括手機(jī)MAC地址,手機(jī)內(nèi)存大小,手機(jī)存儲空間大小,手機(jī)CPU信息等手機(jī)硬件基本信息2018-09-09