Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package ru.mos.portal.android.design;
- import android.content.Context;
- import android.support.annotation.NonNull;
- import android.support.annotation.Nullable;
- import android.support.v4.view.MotionEventCompat;
- import android.support.v4.view.ViewCompat;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.widget.AbsListView;
- import android.widget.FrameLayout;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Makes getting overscroll value from ListViews and other scrollables easy
- * Just add it as parent of whatever scrollable you have.
- * @author me@cab404.ru
- */
- public class BlockingOverscrollView extends FrameLayout {
- private static final String TAG = "BlockingOverscrollView";
- private final static int
- STATE_IDLE = 0,
- STATE_DETECTING = 1,
- STATE_PULLING = 2,
- STATE_SETTLING = 3;
- private View mTarget;
- private float mOverscrollDirection;
- private int mState;
- private float mTouchSlop;
- private float mDragStart;
- private float mPullHeight = 500;
- private float mSettlingSpeed = 0.7f;
- private float mSettlingThreshold = 0.01f;
- private float mProgress = 0f;
- public interface DragListener {
- void onDrag(float amount);
- }
- private List<DragListener> dragListeners = new ArrayList<>();
- public void addDragListener(DragListener listener) {
- dragListeners.add(listener);
- }
- public void removeDragListener(DragListener listener) {
- dragListeners.remove(listener);
- }
- public BlockingOverscrollView(@NonNull Context context) {
- super(context, null);
- }
- public BlockingOverscrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- Log.d(TAG, "BlockingOverscrollView: " + mTouchSlop);
- mPullHeight *= context.getResources().getDisplayMetrics().density;
- }
- void ensureTarget() {
- mTarget = getChildAt(0);
- }
- private boolean canScrollUp() {
- return ViewCompat.canScrollVertically(mTarget, -1);
- }
- private boolean canScrollDown() {
- return ViewCompat.canScrollVertically(mTarget, 1);
- }
- void onProgressChange() {
- if (Math.abs(mProgress) < mSettlingThreshold)
- mProgress = 0;
- for (DragListener listener : dragListeners) {
- listener.onDrag(mProgress);
- }
- }
- void calculateProgress(float unconsumedY) {
- mProgress = unconsumedY / mPullHeight;
- if (Math.abs(mProgress) > 1f)
- mProgress = Math.signum(mProgress);
- float overkill = Math.abs(unconsumedY) - mPullHeight;
- if (overkill > 0)
- mDragStart -= overkill * Math.signum(mProgress);
- onProgressChange();
- }
- void drag(float y) {
- final float deltaY = mDragStart - y;
- final float deltaYSignum = Math.signum(deltaY);
- switch (mState) {
- case STATE_DETECTING:
- if (mOverscrollDirection != 0 && Math.abs(deltaY) > mTouchSlop)
- if (deltaYSignum == mOverscrollDirection) {
- mDragStart = y;
- mState = STATE_PULLING;
- }
- break;
- case STATE_PULLING:
- if (deltaYSignum == mOverscrollDirection)
- calculateProgress(deltaY);
- break;
- case STATE_IDLE:
- mDragStart = y;
- mState = STATE_DETECTING;
- break;
- }
- }
- private void onFinishDrag() {
- if (Math.abs(mProgress) < mSettlingThreshold)
- mState = STATE_IDLE;
- else {
- mState = STATE_SETTLING;
- postSettler();
- }
- }
- // Copied from SwipeRefreshLayout
- @Override
- public void requestDisallowInterceptTouchEvent(boolean b) {
- // if this is a List < L or another view that doesn't support nested
- // scrolling, ignore this request so that the vertical scroll event
- // isn't stolen
- if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
- || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
- // Nope.
- } else {
- super.requestDisallowInterceptTouchEvent(b);
- }
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- ensureTarget();
- final int action = MotionEventCompat.getActionMasked(ev);
- if (mState == STATE_SETTLING) {
- // Intercepting before client does something bad
- return true;
- }
- if (action == MotionEvent.ACTION_DOWN) onFinishDrag();
- if (action == MotionEvent.ACTION_MOVE) {
- if (!canScrollUp())
- mOverscrollDirection = -1;
- else if (!canScrollDown())
- mOverscrollDirection = 1;
- else
- mOverscrollDirection = 0;
- drag(ev.getY());
- }
- return mState == STATE_PULLING;
- }
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- final int action = ev.getActionMasked();
- if (mState == STATE_SETTLING) {
- // Intercepting before client does something bad
- return true;
- }
- switch (mState) {
- case STATE_PULLING:
- if (action == MotionEvent.ACTION_MOVE) {
- drag(ev.getY());
- }
- break;
- }
- if (action == MotionEvent.ACTION_UP) onFinishDrag();
- return true;
- }
- Runnable settler = new Runnable() {
- @Override
- public void run() {
- mProgress *= mSettlingSpeed;
- onProgressChange();
- if (Math.abs(mProgress) > mSettlingThreshold)
- postSettler();
- else
- mState = STATE_IDLE;
- }
- };
- public void postSettler() {
- ViewCompat.postOnAnimation(this, settler);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement