Skip to content

Instantly share code, notes, and snippets.

@fatihsokmen
Created March 21, 2017 11:26
Show Gist options
  • Save fatihsokmen/7a3d5b1853d62e50339a004365fe1a63 to your computer and use it in GitHub Desktop.
Save fatihsokmen/7a3d5b1853d62e50339a004365fe1a63 to your computer and use it in GitHub Desktop.
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
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.support.v4.widget.ViewDragHelper;
import android.support.v4.widget.ViewDragHelper.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
public class PullDismissViewLayout extends FrameLayout {
private ViewDragHelper dragHelper;
private float minFlingVelocity;
private PullDismissViewLayout.Listener listener;
public PullDismissViewLayout(@NonNull Context context) {
super(context);
init(context);
}
public PullDismissViewLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PullDismissViewLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
@TargetApi(21)
public PullDismissViewLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle, int defResStyle) {
super(context, attrs, defStyle, defResStyle);
init(context);
}
private void init(@NonNull Context context) {
if (!this.isInEditMode()) {
ViewConfiguration vc = ViewConfiguration.get(context);
this.minFlingVelocity = (float) vc.getScaledMinimumFlingVelocity();
this.dragHelper = ViewDragHelper.create(this, new PullDismissViewLayout.ViewDragCallback());
}
}
public void setMinFlingVelocity(float minFlingVelocity) {
this.minFlingVelocity = minFlingVelocity;
}
public float getMinFlingVelocity() {
return this.minFlingVelocity;
}
public void setListener(PullDismissViewLayout.Listener listener) {
this.listener = listener;
}
public void computeScroll() {
super.computeScroll();
if (this.dragHelper != null && this.dragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
private float deltaY;
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
boolean pullDown = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
deltaY = event.getY();
case MotionEvent.ACTION_MOVE:
final float dy = event.getY() - deltaY;
if (dy > dragHelper.getTouchSlop()) {
pullDown = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
deltaY = 0.0f;
break;
}
if (!this.dragHelper.shouldInterceptTouchEvent(event) && pullDown) {
if (this.dragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE &&
MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_MOVE &&
this.dragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL)) {
View child = getChildAt(0);
if (child != null && !listener.shouldInterceptTouchEvent(event)) {
this.dragHelper.captureChildView(child, event.getPointerId(0));
return this.dragHelper.getViewDragState() == ViewDragHelper.STATE_DRAGGING;
}
}
}
return false;
}
public boolean onTouchEvent(MotionEvent event) {
this.dragHelper.processTouchEvent(event);
return this.dragHelper.getCapturedView() != null;
}
private class ViewDragCallback extends Callback {
private int startTop;
private float dragPercent;
private View capturedView;
private boolean dismissed;
private ViewDragCallback() {
this.dragPercent = 0.0F;
this.dismissed = false;
}
public boolean tryCaptureView(View view, int i) {
return this.capturedView == null;
}
public int clampViewPositionVertical(View child, int top, int dy) {
if (top < 0) {
return 0;
}
if (dismissed) {
return top;
}
return top;
}
public void onViewCaptured(View view, int activePointerId) {
this.capturedView = view;
this.startTop = view.getTop();
this.dragPercent = 0.0F;
this.dismissed = false;
}
@SuppressLint({"NewApi"})
public void onViewPositionChanged(View view, int left, int top, int dx, int dy) {
int range = PullDismissViewLayout.this.getHeight();
int moved = Math.abs(top - this.startTop);
if (range > 0) {
this.dragPercent = (float) moved / (float) range;
}
view.setAlpha(1.0F - this.dragPercent);
PullDismissViewLayout.this.invalidate();
}
public void onViewDragStateChanged(int state) {
if (this.capturedView != null) {
synchronized (this) {
if (PullDismissViewLayout.this.listener != null) {
PullDismissViewLayout.this.listener.onDragStateChanged(this.capturedView, state);
}
if (state == ViewDragHelper.STATE_IDLE) {
if (this.dismissed) {
PullDismissViewLayout.this.removeView(this.capturedView);
if (PullDismissViewLayout.this.listener != null) {
PullDismissViewLayout.this.listener.onDismissed(this.capturedView);
}
}
this.capturedView = null;
}
}
}
}
public void onViewReleased(View view, float xv, float yv) {
this.dismissed = this.dragPercent >= 0.50F ||
(Math.abs(xv) > PullDismissViewLayout.this.minFlingVelocity && this.dragPercent > 0.20F);
int finalTop = dismissed ? getHeight() : startTop;
PullDismissViewLayout.this.dragHelper.settleCapturedViewAt(0, finalTop);
PullDismissViewLayout.this.invalidate();
}
}
public interface Listener {
void onDismissed(View view);
void onDragStateChanged(View view, int state);
boolean shouldInterceptTouchEvent(MotionEvent event);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment