1. 程式人生 > >Android 帶阻尼回彈效果的ScorllView

Android 帶阻尼回彈效果的ScorllView

import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import
android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.animation.AnimationUtils; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; import android.
widget.Scroller; import android.view.View.OnTouchListener; import java.lang.reflect.Field; import java.util.List; /** * Layout container for a view hierarchy that can be scrolled by the user, * allowing it to be larger than the physical display. A ScrollView is a * {@link FrameLayout}, meaning you should place one child in it containing the * entire contents to scroll; this child may itself be a layout manager with a * complex hierarchy of objects. A child that is often used is a * {@link android.widget.LinearLayout} in a vertical orientation, presenting a vertical array * of top-level items that the user can scroll through. * <p> * The {@link android.widget.TextView} class also takes care of its own scrolling, so does not * require a ScrollView, but using the two together is possible to achieve the * effect of a text view within a larger container. * <p> * ScrollView only supports vertical scrolling. * <br/>仿Iphone的自動回彈動化的scrollView,直接做為容器像一般scrollView一樣使用即可 */
public class OverScrollView extends FrameLayout implements OnTouchListener { static final int ANIMATED_SCROLL_GAP = 250; static final float MAX_SCROLL_FACTOR = 0.5f; static final float OVERSHOOT_TENSION = 0.75f; private long mLastScroll; private final Rect mTempRect = new Rect(); private Scroller mScroller; protected Context mContext; Field mScrollYField; Field mScrollXField; boolean hasFailedObtainingScrollFields; int prevScrollY; boolean isInFlingMode = false; DisplayMetrics metrics; LayoutInflater inflater; protected View child; private Runnable overScrollerSpringbackTask; /** * Flag to indicate that we are moving focus ourselves. This is so the code * that watches for focus changes initiated outside this ScrollView knows * that it does not have to do anything. */ private boolean mScrollViewMovedFocus; /** * Position of the last motion event. */ private float mLastMotionY; /** * True when the layout has changed but the traversal has not come through * yet. Ideally the view hierarchy would keep track of this for us. */ private boolean mIsLayoutDirty = true; /** * The child to give focus to in the event that a child has requested focus * while the layout is dirty. This prevents the scroll from being wrong if * the child has not been laid out before requesting focus. */ private View mChildToScrollTo = null; /** * True if the user is currently dragging this ScrollView around. This is * not the same as 'is being flinged', which can be checked by * mScroller.isFinished() (flinging begins when the user lifts his finger). */ private boolean mIsBeingDragged = false; /** * Determines speed during touch scrolling */ private VelocityTracker mVelocityTracker; /** * When set to true, the scroll view measure its child to make it fill the * currently visible area. */ private boolean mFillViewport; /** * Whether arrow scrolling is animated. */ private boolean mSmoothScrollingEnabled = true; private int mTouchSlop; private int mMinimumVelocity; private int mMaximumVelocity; /** * ID of the active pointer. This is used to retain consistency during * drags/flings if multiple pointers are used. */ private int mActivePointerId = INVALID_POINTER; /** * Sentinel value for no current active pointer. Used by * {@link #mActivePointerId}. */ private static final int INVALID_POINTER = -1; private OnScrollChangedListener onScrollChangedListener; public OverScrollView(Context context) { this(context, null); } public OverScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public OverScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; initScrollView(); setFillViewport(true); initBounce(); } private void initBounce() { metrics = this.mContext.getResources().getDisplayMetrics(); // init the bouncy scroller, and make sure the layout is being drawn // after the top padding mScroller = new Scroller(getContext(), new OvershootInterpolator(OVERSHOOT_TENSION)); overScrollerSpringbackTask = new Runnable() { @Override public void run() { // scroll till after the padding mScroller.computeScrollOffset(); scrollTo(0, mScroller.getCurrY()); if (!mScroller.isFinished()) { post(this); } } }; prevScrollY = getPaddingTop(); try { mScrollXField = View.class.getDeclaredField("mScrollX"); mScrollYField = View.class.getDeclaredField("mScrollY"); } catch (Exception e) { hasFailedObtainingScrollFields = true; } } private void SetScrollY(int value) { if (mScrollYField != null) { try { mScrollYField.setInt(this, value); } catch (Exception e) { } } } private void SetScrollX(int value) { if (mScrollXField != null) { try { mScrollXField.setInt(this, value); } catch (Exception e) { } } } public void initChildPointer() { child = getChildAt(0); child.setPadding(0, 1500, 0, 1500); } @Override protected float getTopFadingEdgeStrength() { if (getChildCount() == 0) { return 0.0f; } final int length = getVerticalFadingEdgeLength(); if (getScrollY() < length) { return getScrollY() / (float) length; } return 1.0f; } @Override protected float getBottomFadingEdgeStrength() { if (getChildCount() == 0) { return 0.0f; } final int length = getVerticalFadingEdgeLength(); final int bottomEdge = getHeight() - getPaddingBottom(); final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge; if (span < length) { return span / (float) length; } return 1.0f; } /** * @return The maximum amount this scroll view will scroll in response to an * arrow event. */ public int getMaxScrollAmount() { return (int) (MAX_SCROLL_FACTOR * (getBottom() - getTop())); } private void initScrollView() { mScroller = new Scroller(getContext()); setFocusable(true); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setWillNotDraw(false); final ViewConfiguration configuration = ViewConfiguration.get(mContext); mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); setOnTouchListener(this); post(new Runnable() { public void run() { scrollTo(0, child.getPaddingTop()); } }); } @Override public void addView(View child) { if (getChildCount() > 0) { throw new IllegalStateException("ScrollView can host only one direct child"); } super.addView(child); initChildPointer(); } @Override public void addView(View child, int index) { if (getChildCount() > 0) { throw new IllegalStateException("ScrollView can host only one direct child"); } super.addView(child, index); initChildPointer(); } @Override public void addView(View child, ViewGroup.LayoutParams params) { if (getChildCount() > 0) { throw new IllegalStateException("ScrollView can host only one direct child"); } super.addView(child, params); initChildPointer(); } @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { if (getChildCount() > 0) { throw new IllegalStateException("ScrollView can host only one direct child"); } super.addView(child, index, params); } /** * @return Returns true this ScrollView can be scrolled */ private boolean canScroll() { View child = getChildAt(0); if (child != null) { int childHeight = child.getHeight(); return getHeight() < childHeight + getPaddingTop() + getPaddingBottom(); } return false; } /** * Indicates whether this ScrollView's content is stretched to fill the * viewport. * * @return True if the content fills the viewport, false otherwise. */ public boolean isFillViewport() { return mFillViewport; } /** * Indicates this ScrollView whether it should stretch its content height to * fill the viewport or not. * * @param fillViewport True to stretch the content's height to the * viewport's boundaries, false otherwise. */ public void setFillViewport(boolean fillViewport) { if (fillViewport != mFillViewport) { mFillViewport = fillViewport; requestLayout(); } } /** * @return Whether arrow scrolling will animate its transition. */ public boolean isSmoothScrollingEnabled() { return mSmoothScrollingEnabled; } /** * Set whether arrow scrolling will animate its transition. * * @param smoothScrollingEnabled whether arrow scrolling will animate its * transition */ public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) { mSmoothScrollingEnabled = smoothScrollingEnabled; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!mFillViewport) { return; } final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.UNSPECIFIED) { return; } if (getChildCount() > 0) { final View child = getChildAt(0); int height = getMeasuredHeight(); if (child.getMeasuredHeight() < height) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); height -= getPaddingTop(); height -= getPaddingBottom(); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } } @Override public boolean dispatchKeyEvent(KeyEvent event) { // Let the focused view and/or our descendants get the key first return super.dispatchKeyEvent(event) || executeKeyEvent(event); } /** * You can call this function yourself to have the scroll view perform * scrolling from a key event, just as if the event had been dispatched to * it by the view hierarchy. * * @param event The key event to execute. * @return Return true if the event was handled, else false. */ public boolean executeKeyEvent(KeyEvent event) { mTempRect.setEmpty(); if (!canScroll()) { if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) { View currentFocused = findFocus(); if (currentFocused == this) currentFocused = null; View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, View.FOCUS_DOWN); return nextFocused != null && nextFocused != this && nextFocused.requestFocus(View.FOCUS_DOWN); } return false; } boolean handled = false; if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_UP: if (!event.isAltPressed()) { handled = arrowScroll(View.FOCUS_UP); } else { handled = fullScroll(View.FOCUS_UP); } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (!event.isAltPressed()) { handled = arrowScroll(View.FOCUS_DOWN); } else { handled = fullScroll(View.FOCUS_DOWN); } break; case KeyEvent.KEYCODE_SPACE: pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN); break; } } return handled; } public boolean inChild(int x, int y) { if (getChildCount() > 0) { final int scrollY = getScrollY(); final View child = getChildAt(0); return !(y < child.getTop() - scrollY || y >= child.getBottom() - scrollY || x < child.getLeft() || x >= child .getRight()); } return false; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onMotionEvent will be called and we do the actual * scrolling there. */ /* * Shortcut the most recurring case: the user is in the dragging state * and he is moving his finger. We want to intercept this motion. */ final int action = ev

相關推薦

Android 阻尼效果ScorllView

import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; impo

ScrollView實現阻尼效果

今天跟大夥簡紹個ScrollView的阻尼回彈!下拉到一定程度,可以回撥進行重新整理和進行操作等! 直接上程式碼了! package com.***.fb**.widget; import android.content.Context; import

android ListView 仿IOS 效果

最近看IOS的下拉效果感覺很不錯,當拉倒最上面和最下面的時候繼續拉動會有緩衝,想在android裡面也做一個,到網上到處找,沒有找到好的方法,據說android新的API對ListView有這樣的支援,感覺不是特別好用。 自己利用scroller實現了一下,廢話不多說了直接

Android HorizontalScrollView效果

轉載記錄備份查閱 import android.annotation.SuppressLint; import android.os.Build; import android.util.Log; import android.view.MotionEvent

Android滑動效果

原理: addHeaderView裡做的事: 1.測量出header的寬高,呼叫了measureView方法 2.設定LayoutParams,寬:MATCH_PARENT,高:10 3.設定topMargin的值為負的header的高度,即將header隱藏在螢幕最上方

Android中自定義仿IOS效果的ListView

ios中有一個控制元件回彈的效果,比如listview ,拉動到第一條或者最後一條資料的時候,還可以繼續拉動,鬆手就回彈到原來位置,很贊,其實在android中實現起來也非常簡單,我們只需要重寫listview的下面兩個 方法即可 先上圖 @Override pub

Android 自定義ScrollView 支援慣性滑動,慣性效果。支援上拉載入更多

先講下原理: ScrollView的子View 主要分為3部分:head頭部,滾動內容,fooder底部 我們實現慣性滑動,以及回彈,都是靠超過head或者fooder 就重新滾動到  ,內容的頂部或者底部。 之前看了Pulltorefresh 他是通過不斷改變 head或

android仿IOS頁面效果

碼農同學們做過手機開發的相比一定對ios平臺頁面的上下回彈效果印象深刻,特別是android開發對此是各種嫉妒羨慕恨啊,在特效方面蘋果做的很好,做了很多,當然谷歌也不差,神馬特效咱們都能做出來,因為android開放更多,移植性也更好,so..我特麼手賤自己寫了一個上下拉動

Android ScrollView效果(二)

上一篇文章說了那個效果不怎麼好,現在實現方法稍微變一下: import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.

自定義滑動距離監控和仿iOS效果的NestedScrollView

  在最近的Support Library更新中(25.3.0),新增或者修復了許多東西,具體可以看revisions,其中有一個新增的動畫效果:SpringAnimation 即彈簧動畫,SpringAnimation是一個受SpringForce驅動的動畫。彈簧力限定

-webkit-overflow-scrolling 與滾動效果.

插件 列表 卡住 快的 優雅 css 移動設備 分享 兼容性 參考來源:https://developer.mozilla.org/zh-CN/docs/Web/CSS/-webkit-overflow-scrolling      https://www.w3cways.

angual+mui 雙欄上拉加載,微信裏面禁用默認事件可用,可以防止瀏覽器效果

apply length data mui this reat mobile ng- a10 //html 部分 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 15.0px Consolas; color: #2eafa9

自定義ScrollView 實現上拉下拉的效果--並且子控件中有Viewpager的情況

是否 AS abs pri tar utils lda animation ted onInterceptTouchEvent就是對子控件中Viewpager的處理:左右滑動應該讓viewpager消費 1 public class MyScrollView ext

ios 下防止整個網頁滑動(阻尼 . 瞞天過海,騙IOS,把阻尼限制在滾動區div內

list ont 16px 一點 width sta 常見 沒有 手機app 下面是一個手機APP頁面,分成上中下三部分,最上面和最下面是固定的,中間可以滾動。這是常見的APP布局方式。 <style> .box{ overflow: auto; -

解決蘋果微信瀏覽器下拉效果

.content是需要滑動的部分 var overscroll = function(el) { el.addEventListener('touchstart', function()

移動端阻止瀏覽器中預設元素滑動效果(橡皮筋效果

在js檔案中加如下程式碼: document.addEventListener('touchstart',function(e){ e.preventDefault(); //

JavaScript禁止微信瀏覽器下拉效果

本文例項為大家分享了JavaScript禁止微信瀏覽器下拉回彈的效果 方法1:         <script type="text/javascript">             var overscroll = function(el){     

原來操控介面可以這麼簡單----安卓上下滑動縮放頂部圖片,左右滑動結束當前Activity,及View柔和效果

先上效果圖: 上傳圖片不能超過2M,費了好大勁。每一張gif動的有點快,將就看。 首先說原理: 為activity的xml檔案根佈局新增setOnTouchListener。上下滑動和左右滑動的所有操作都是在OnTouchListener的onTouch方法中實現的,通過

Android ListView滑動——overScrollBy

註解  /**      * Scroll the view with standard behavior for scrolling beyond the normal      * content boundaries. Views that call this met

實現ViewPager的效果

為了能夠在ViewPager的第一頁和最後一頁左右滑動時候不顯得那麼生硬,通過重寫ViewPager類實現回彈效果。 程式碼很簡單,主要重寫onTouchEvent方法。 程式碼如下: public class BounceBackViewPager