mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 02:16:51 +02:00
Android - People - Swipe to reveal delete + layout adaptations for design
This commit is contained in:
parent
1a261bea04
commit
60338f9899
4 changed files with 810 additions and 20 deletions
|
@ -0,0 +1,729 @@
|
||||||
|
package io.highfidelity.hifiinterface.view;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.view.GestureDetectorCompat;
|
||||||
|
import android.support.v4.view.ViewCompat;
|
||||||
|
import android.support.v4.widget.ViewDragHelper;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import io.highfidelity.hifiinterface.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Mark O'Sullivan on 25th February 2018.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Mark O'Sullivan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SuppressLint("RtlHardcoded")
|
||||||
|
public class SwipeRevealLayout extends ViewGroup {
|
||||||
|
|
||||||
|
private static final String SUPER_INSTANCE_STATE = "saved_instance_state_parcelable";
|
||||||
|
|
||||||
|
private static final int DEFAULT_MIN_FLING_VELOCITY = 300; // dp per second
|
||||||
|
private static final int DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT = 1; // dp
|
||||||
|
|
||||||
|
public static final int DRAG_EDGE_LEFT = 0x1;
|
||||||
|
public static final int DRAG_EDGE_RIGHT = 0x1 << 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The secondary view will be under the main view.
|
||||||
|
*/
|
||||||
|
public static final int MODE_NORMAL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The secondary view will stick the edge of the main view.
|
||||||
|
*/
|
||||||
|
public static final int MODE_SAME_LEVEL = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main view is the view which is shown when the layout is closed.
|
||||||
|
*/
|
||||||
|
private View mMainView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secondary view is the view which is shown when the layout is opened.
|
||||||
|
*/
|
||||||
|
private View mSecondaryView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rectangle position of the main view when the layout is closed.
|
||||||
|
*/
|
||||||
|
private Rect mRectMainClose = new Rect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rectangle position of the main view when the layout is opened.
|
||||||
|
*/
|
||||||
|
private Rect mRectMainOpen = new Rect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rectangle position of the secondary view when the layout is closed.
|
||||||
|
*/
|
||||||
|
private Rect mRectSecClose = new Rect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rectangle position of the secondary view when the layout is opened.
|
||||||
|
*/
|
||||||
|
private Rect mRectSecOpen = new Rect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum distance (px) to the closest drag edge that the SwipeRevealLayout
|
||||||
|
* will disallow the parent to intercept touch event.
|
||||||
|
*/
|
||||||
|
private int mMinDistRequestDisallowParent = 0;
|
||||||
|
|
||||||
|
private boolean mIsOpenBeforeInit = false;
|
||||||
|
private volatile boolean mIsScrolling = false;
|
||||||
|
private volatile boolean mLockDrag = false;
|
||||||
|
|
||||||
|
private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
|
||||||
|
private int mMode = MODE_NORMAL;
|
||||||
|
|
||||||
|
private int mDragEdge = DRAG_EDGE_LEFT;
|
||||||
|
|
||||||
|
private float mDragDist = 0;
|
||||||
|
private float mPrevX = -1;
|
||||||
|
|
||||||
|
private ViewDragHelper mDragHelper;
|
||||||
|
private GestureDetectorCompat mGestureDetector;
|
||||||
|
|
||||||
|
public SwipeRevealLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwipeRevealLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwipeRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState());
|
||||||
|
return super.onSaveInstanceState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state) {
|
||||||
|
Bundle bundle = (Bundle) state;
|
||||||
|
state = bundle.getParcelable(SUPER_INSTANCE_STATE);
|
||||||
|
super.onRestoreInstanceState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
mGestureDetector.onTouchEvent(event);
|
||||||
|
mDragHelper.processTouchEvent(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||||
|
if (isDragLocked()) {
|
||||||
|
return super.onInterceptTouchEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDragHelper.processTouchEvent(ev);
|
||||||
|
mGestureDetector.onTouchEvent(ev);
|
||||||
|
accumulateDragDist(ev);
|
||||||
|
|
||||||
|
boolean couldBecomeClick = couldBecomeClick(ev);
|
||||||
|
boolean settling = mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING;
|
||||||
|
boolean idleAfterScrolled = mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE
|
||||||
|
&& mIsScrolling;
|
||||||
|
|
||||||
|
// must be placed as the last statement
|
||||||
|
mPrevX = ev.getX();
|
||||||
|
|
||||||
|
// return true => intercept, cannot trigger onClick event
|
||||||
|
return !couldBecomeClick && (settling || idleAfterScrolled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
|
||||||
|
// get views
|
||||||
|
if (getChildCount() >= 2) {
|
||||||
|
mSecondaryView = getChildAt(0);
|
||||||
|
mMainView = getChildAt(1);
|
||||||
|
}
|
||||||
|
else if (getChildCount() == 1) {
|
||||||
|
mMainView = getChildAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
for (int index = 0; index < getChildCount(); index++) {
|
||||||
|
final View child = getChildAt(index);
|
||||||
|
|
||||||
|
int left, right, top, bottom;
|
||||||
|
left = right = top = bottom = 0;
|
||||||
|
|
||||||
|
final int minLeft = getPaddingLeft();
|
||||||
|
final int maxRight = Math.max(r - getPaddingRight() - l, 0);
|
||||||
|
final int minTop = getPaddingTop();
|
||||||
|
final int maxBottom = Math.max(b - getPaddingBottom() - t, 0);
|
||||||
|
|
||||||
|
int measuredChildHeight = child.getMeasuredHeight();
|
||||||
|
int measuredChildWidth = child.getMeasuredWidth();
|
||||||
|
|
||||||
|
// need to take account if child size is match_parent
|
||||||
|
final LayoutParams childParams = child.getLayoutParams();
|
||||||
|
boolean matchParentHeight = false;
|
||||||
|
boolean matchParentWidth = false;
|
||||||
|
|
||||||
|
if (childParams != null) {
|
||||||
|
matchParentHeight = (childParams.height == LayoutParams.MATCH_PARENT) ||
|
||||||
|
(childParams.height == LayoutParams.FILL_PARENT);
|
||||||
|
matchParentWidth = (childParams.width == LayoutParams.MATCH_PARENT) ||
|
||||||
|
(childParams.width == LayoutParams.FILL_PARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchParentHeight) {
|
||||||
|
measuredChildHeight = maxBottom - minTop;
|
||||||
|
childParams.height = measuredChildHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchParentWidth) {
|
||||||
|
measuredChildWidth = maxRight - minLeft;
|
||||||
|
childParams.width = measuredChildWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
left = Math.max(r - measuredChildWidth - getPaddingRight() - l, minLeft);
|
||||||
|
top = Math.min(getPaddingTop(), maxBottom);
|
||||||
|
right = Math.max(r - getPaddingRight() - l, minLeft);
|
||||||
|
bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
left = Math.min(getPaddingLeft(), maxRight);
|
||||||
|
top = Math.min(getPaddingTop(), maxBottom);
|
||||||
|
right = Math.min(measuredChildWidth + getPaddingLeft(), maxRight);
|
||||||
|
bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
child.layout(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// taking account offset when mode is SAME_LEVEL
|
||||||
|
if (mMode == MODE_SAME_LEVEL) {
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
mSecondaryView.offsetLeftAndRight(-mSecondaryView.getWidth());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
mSecondaryView.offsetLeftAndRight(mSecondaryView.getWidth());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initRects();
|
||||||
|
|
||||||
|
if (mIsOpenBeforeInit) {
|
||||||
|
open(false);
|
||||||
|
} else {
|
||||||
|
close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
if (getChildCount() < 2) {
|
||||||
|
throw new RuntimeException("Layout must have two children");
|
||||||
|
}
|
||||||
|
|
||||||
|
final LayoutParams params = getLayoutParams();
|
||||||
|
|
||||||
|
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||||
|
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||||
|
|
||||||
|
int desiredWidth = 0;
|
||||||
|
int desiredHeight = 0;
|
||||||
|
|
||||||
|
// first find the largest child
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
final View child = getChildAt(i);
|
||||||
|
measureChild(child, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth);
|
||||||
|
desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight);
|
||||||
|
}
|
||||||
|
// create new measure spec using the largest child width
|
||||||
|
widthMeasureSpec = MeasureSpec.makeMeasureSpec(desiredWidth, widthMode);
|
||||||
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, heightMode);
|
||||||
|
|
||||||
|
final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
final View child = getChildAt(i);
|
||||||
|
final LayoutParams childParams = child.getLayoutParams();
|
||||||
|
|
||||||
|
if (childParams != null) {
|
||||||
|
if (childParams.height == LayoutParams.MATCH_PARENT) {
|
||||||
|
child.setMinimumHeight(measuredHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childParams.width == LayoutParams.MATCH_PARENT) {
|
||||||
|
child.setMinimumWidth(measuredWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
measureChild(child, widthMeasureSpec, heightMeasureSpec);
|
||||||
|
desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth);
|
||||||
|
desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// taking accounts of padding
|
||||||
|
desiredWidth += getPaddingLeft() + getPaddingRight();
|
||||||
|
desiredHeight += getPaddingTop() + getPaddingBottom();
|
||||||
|
|
||||||
|
// adjust desired width
|
||||||
|
if (widthMode == MeasureSpec.EXACTLY) {
|
||||||
|
desiredWidth = measuredWidth;
|
||||||
|
} else {
|
||||||
|
if (params.width == LayoutParams.MATCH_PARENT) {
|
||||||
|
desiredWidth = measuredWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widthMode == MeasureSpec.AT_MOST) {
|
||||||
|
desiredWidth = (desiredWidth > measuredWidth)? measuredWidth : desiredWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust desired height
|
||||||
|
if (heightMode == MeasureSpec.EXACTLY) {
|
||||||
|
desiredHeight = measuredHeight;
|
||||||
|
} else {
|
||||||
|
if (params.height == LayoutParams.MATCH_PARENT) {
|
||||||
|
desiredHeight = measuredHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heightMode == MeasureSpec.AT_MOST) {
|
||||||
|
desiredHeight = (desiredHeight > measuredHeight)? measuredHeight : desiredHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(desiredWidth, desiredHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void computeScroll() {
|
||||||
|
if (mDragHelper.continueSettling(true)) {
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the panel to show the secondary view
|
||||||
|
*/
|
||||||
|
public void open(boolean animation) {
|
||||||
|
mIsOpenBeforeInit = true;
|
||||||
|
|
||||||
|
if (animation) {
|
||||||
|
mDragHelper.smoothSlideViewTo(mMainView, mRectMainOpen.left, mRectMainOpen.top);
|
||||||
|
} else {
|
||||||
|
mDragHelper.abort();
|
||||||
|
|
||||||
|
mMainView.layout(
|
||||||
|
mRectMainOpen.left,
|
||||||
|
mRectMainOpen.top,
|
||||||
|
mRectMainOpen.right,
|
||||||
|
mRectMainOpen.bottom
|
||||||
|
);
|
||||||
|
|
||||||
|
mSecondaryView.layout(
|
||||||
|
mRectSecOpen.left,
|
||||||
|
mRectSecOpen.top,
|
||||||
|
mRectSecOpen.right,
|
||||||
|
mRectSecOpen.bottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the panel to hide the secondary view
|
||||||
|
*/
|
||||||
|
public void close(boolean animation) {
|
||||||
|
mIsOpenBeforeInit = false;
|
||||||
|
|
||||||
|
if (animation) {
|
||||||
|
mDragHelper.smoothSlideViewTo(mMainView, mRectMainClose.left, mRectMainClose.top);
|
||||||
|
} else {
|
||||||
|
mDragHelper.abort();
|
||||||
|
mMainView.layout(
|
||||||
|
mRectMainClose.left,
|
||||||
|
mRectMainClose.top,
|
||||||
|
mRectMainClose.right,
|
||||||
|
mRectMainClose.bottom
|
||||||
|
);
|
||||||
|
mSecondaryView.layout(
|
||||||
|
mRectSecClose.left,
|
||||||
|
mRectSecClose.top,
|
||||||
|
mRectSecClose.right,
|
||||||
|
mRectSecClose.bottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the drag/swipe motion is currently locked.
|
||||||
|
*/
|
||||||
|
public boolean isDragLocked() {
|
||||||
|
return mLockDrag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMainOpenLeft() {
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
return mRectMainClose.left + mSecondaryView.getWidth();
|
||||||
|
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
return mRectMainClose.left - mSecondaryView.getWidth();
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMainOpenTop() {
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
return mRectMainClose.top;
|
||||||
|
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
return mRectMainClose.top;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSecOpenLeft() {
|
||||||
|
return mRectSecClose.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSecOpenTop() {
|
||||||
|
return mRectSecClose.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initRects() {
|
||||||
|
// close position of main view
|
||||||
|
mRectMainClose.set(
|
||||||
|
mMainView.getLeft(),
|
||||||
|
mMainView.getTop(),
|
||||||
|
mMainView.getRight(),
|
||||||
|
mMainView.getBottom()
|
||||||
|
);
|
||||||
|
|
||||||
|
// close position of secondary view
|
||||||
|
mRectSecClose.set(
|
||||||
|
mSecondaryView.getLeft(),
|
||||||
|
mSecondaryView.getTop(),
|
||||||
|
mSecondaryView.getRight(),
|
||||||
|
mSecondaryView.getBottom()
|
||||||
|
);
|
||||||
|
|
||||||
|
// open position of the main view
|
||||||
|
mRectMainOpen.set(
|
||||||
|
getMainOpenLeft(),
|
||||||
|
getMainOpenTop(),
|
||||||
|
getMainOpenLeft() + mMainView.getWidth(),
|
||||||
|
getMainOpenTop() + mMainView.getHeight()
|
||||||
|
);
|
||||||
|
|
||||||
|
// open position of the secondary view
|
||||||
|
mRectSecOpen.set(
|
||||||
|
getSecOpenLeft(),
|
||||||
|
getSecOpenTop(),
|
||||||
|
getSecOpenLeft() + mSecondaryView.getWidth(),
|
||||||
|
getSecOpenTop() + mSecondaryView.getHeight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean couldBecomeClick(MotionEvent ev) {
|
||||||
|
return isInMainView(ev) && !shouldInitiateADrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInMainView(MotionEvent ev) {
|
||||||
|
float x = ev.getX();
|
||||||
|
float y = ev.getY();
|
||||||
|
|
||||||
|
boolean withinVertical = mMainView.getTop() <= y && y <= mMainView.getBottom();
|
||||||
|
boolean withinHorizontal = mMainView.getLeft() <= x && x <= mMainView.getRight();
|
||||||
|
|
||||||
|
return withinVertical && withinHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldInitiateADrag() {
|
||||||
|
float minDistToInitiateDrag = mDragHelper.getTouchSlop();
|
||||||
|
return mDragDist >= minDistToInitiateDrag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void accumulateDragDist(MotionEvent ev) {
|
||||||
|
final int action = ev.getAction();
|
||||||
|
if (action == MotionEvent.ACTION_DOWN) {
|
||||||
|
mDragDist = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dragged = Math.abs(ev.getX() - mPrevX);
|
||||||
|
|
||||||
|
mDragDist += dragged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context, AttributeSet attrs) {
|
||||||
|
if (attrs != null && context != null) {
|
||||||
|
TypedArray a = context.getTheme().obtainStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
R.styleable.SwipeRevealLayout,
|
||||||
|
0, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
mDragEdge = a.getInteger(R.styleable.SwipeRevealLayout_dragFromEdge, DRAG_EDGE_LEFT);
|
||||||
|
mMode = MODE_NORMAL;
|
||||||
|
mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
|
||||||
|
mMinDistRequestDisallowParent = DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDragHelper = ViewDragHelper.create(this, 1.0f, mDragHelperCallback);
|
||||||
|
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
|
||||||
|
|
||||||
|
mGestureDetector = new GestureDetectorCompat(context, mGestureListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
|
||||||
|
boolean hasDisallowed = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onDown(MotionEvent e) {
|
||||||
|
mIsScrolling = false;
|
||||||
|
hasDisallowed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||||
|
mIsScrolling = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
|
mIsScrolling = true;
|
||||||
|
|
||||||
|
if (getParent() != null) {
|
||||||
|
boolean shouldDisallow;
|
||||||
|
|
||||||
|
if (!hasDisallowed) {
|
||||||
|
shouldDisallow = getDistToClosestEdge() >= mMinDistRequestDisallowParent;
|
||||||
|
if (shouldDisallow) {
|
||||||
|
hasDisallowed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shouldDisallow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disallow parent to intercept touch event so that the layout will work
|
||||||
|
// properly on RecyclerView or view that handles scroll gesture.
|
||||||
|
getParent().requestDisallowInterceptTouchEvent(shouldDisallow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private int getDistToClosestEdge() {
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
final int pivotRight = mRectMainClose.left + mSecondaryView.getWidth();
|
||||||
|
|
||||||
|
return Math.min(
|
||||||
|
mMainView.getLeft() - mRectMainClose.left,
|
||||||
|
pivotRight - mMainView.getLeft()
|
||||||
|
);
|
||||||
|
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
final int pivotLeft = mRectMainClose.right - mSecondaryView.getWidth();
|
||||||
|
|
||||||
|
return Math.min(
|
||||||
|
mMainView.getRight() - pivotLeft,
|
||||||
|
mRectMainClose.right - mMainView.getRight()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getHalfwayPivotHorizontal() {
|
||||||
|
if (mDragEdge == DRAG_EDGE_LEFT) {
|
||||||
|
return mRectMainClose.left + mSecondaryView.getWidth() / 2;
|
||||||
|
} else {
|
||||||
|
return mRectMainClose.right - mSecondaryView.getWidth() / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() {
|
||||||
|
@Override
|
||||||
|
public boolean tryCaptureView(View child, int pointerId) {
|
||||||
|
|
||||||
|
if (mLockDrag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mDragHelper.captureChildView(mMainView, pointerId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int clampViewPositionHorizontal(View child, int left, int dx) {
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
return Math.max(
|
||||||
|
Math.min(left, mRectMainClose.left),
|
||||||
|
mRectMainClose.left - mSecondaryView.getWidth()
|
||||||
|
);
|
||||||
|
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
return Math.max(
|
||||||
|
Math.min(left, mRectMainClose.left + mSecondaryView.getWidth()),
|
||||||
|
mRectMainClose.left
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return child.getLeft();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewReleased(View releasedChild, float xvel, float yvel) {
|
||||||
|
final boolean velRightExceeded = pxToDp((int) xvel) >= mMinFlingVelocity;
|
||||||
|
final boolean velLeftExceeded = pxToDp((int) xvel) <= -mMinFlingVelocity;
|
||||||
|
|
||||||
|
final int pivotHorizontal = getHalfwayPivotHorizontal();
|
||||||
|
|
||||||
|
switch (mDragEdge) {
|
||||||
|
case DRAG_EDGE_RIGHT:
|
||||||
|
if (velRightExceeded) {
|
||||||
|
close(true);
|
||||||
|
} else if (velLeftExceeded) {
|
||||||
|
open(true);
|
||||||
|
} else {
|
||||||
|
if (mMainView.getRight() < pivotHorizontal) {
|
||||||
|
open(true);
|
||||||
|
} else {
|
||||||
|
close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DRAG_EDGE_LEFT:
|
||||||
|
if (velRightExceeded) {
|
||||||
|
open(true);
|
||||||
|
} else if (velLeftExceeded) {
|
||||||
|
close(true);
|
||||||
|
} else {
|
||||||
|
if (mMainView.getLeft() < pivotHorizontal) {
|
||||||
|
close(true);
|
||||||
|
} else {
|
||||||
|
open(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
|
||||||
|
super.onEdgeDragStarted(edgeFlags, pointerId);
|
||||||
|
|
||||||
|
if (mLockDrag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean edgeStartLeft = (mDragEdge == DRAG_EDGE_RIGHT)
|
||||||
|
&& edgeFlags == ViewDragHelper.EDGE_LEFT;
|
||||||
|
|
||||||
|
boolean edgeStartRight = (mDragEdge == DRAG_EDGE_LEFT)
|
||||||
|
&& edgeFlags == ViewDragHelper.EDGE_RIGHT;
|
||||||
|
|
||||||
|
if (edgeStartLeft || edgeStartRight) {
|
||||||
|
mDragHelper.captureChildView(mMainView, pointerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
|
||||||
|
super.onViewPositionChanged(changedView, left, top, dx, dy);
|
||||||
|
if (mMode == MODE_SAME_LEVEL) {
|
||||||
|
if (mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) {
|
||||||
|
mSecondaryView.offsetLeftAndRight(dx);
|
||||||
|
} else {
|
||||||
|
mSecondaryView.offsetTopAndBottom(dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private int pxToDp(int px) {
|
||||||
|
Resources resources = getContext().getResources();
|
||||||
|
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||||
|
return (int) (px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,11 @@ import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
@ -62,7 +65,14 @@ public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHo
|
||||||
public void onBindViewHolder(UserListAdapter.ViewHolder holder, int position) {
|
public void onBindViewHolder(UserListAdapter.ViewHolder holder, int position) {
|
||||||
User aUser = mUsers.get(position);
|
User aUser = mUsers.get(position);
|
||||||
holder.mUsername.setText(aUser.name);
|
holder.mUsername.setText(aUser.name);
|
||||||
holder.mOnline.setText(aUser.online?"ONLINE":"OFFLINE");
|
holder.mOnline.setText(aUser.online?"Online":"Offline");
|
||||||
|
holder.mOnline.setVisibility(aUser.online? View.VISIBLE : View.GONE);
|
||||||
|
holder.mUserDelete.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Toast.makeText(view.getContext(), "Delete " + aUser.name, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
Uri uri = Uri.parse(aUser.imageUrl);
|
Uri uri = Uri.parse(aUser.imageUrl);
|
||||||
Picasso.get().load(uri).into(holder.mImage);
|
Picasso.get().load(uri).into(holder.mImage);
|
||||||
}
|
}
|
||||||
|
@ -77,12 +87,14 @@ public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHo
|
||||||
TextView mUsername;
|
TextView mUsername;
|
||||||
TextView mOnline;
|
TextView mOnline;
|
||||||
ImageView mImage;
|
ImageView mImage;
|
||||||
|
ImageButton mUserDelete;
|
||||||
|
|
||||||
public ViewHolder(View itemView) {
|
public ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
mUsername = itemView.findViewById(R.id.userName);
|
mUsername = itemView.findViewById(R.id.userName);
|
||||||
mOnline = itemView.findViewById(R.id.userOnline);
|
mOnline = itemView.findViewById(R.id.userOnline);
|
||||||
mImage = itemView.findViewById(R.id.userImage);
|
mImage = itemView.findViewById(R.id.userImage);
|
||||||
|
mUserDelete = itemView.findViewById(R.id.userDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,66 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<io.highfidelity.hifiinterface.view.SwipeRevealLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
android:layout_gravity="center_vertical"
|
||||||
|
app:dragFromEdge="right">
|
||||||
|
|
||||||
<ImageView
|
<FrameLayout
|
||||||
android:id="@+id/userImage"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/userName"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/userImage"/>
|
android:background="@android:color/holo_red_dark">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical|end"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<ImageButton
|
||||||
android:id="@+id/userOnline"
|
android:id="@+id/userDelete"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="50dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:gravity="end"
|
android:background="@android:color/transparent"
|
||||||
app:layout_constraintStart_toEndOf="@id/userName"
|
app:srcCompat="@drawable/ic_clear"
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
android:text="Delete"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/backgroundLight"
|
||||||
|
android:clickable="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/userImage"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/userImage"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/userName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/userOnline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
|
</io.highfidelity.hifiinterface.view.SwipeRevealLayout>
|
9
android/app/src/main/res/values/attrs.xml
Normal file
9
android/app/src/main/res/values/attrs.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="SwipeRevealLayout">
|
||||||
|
<attr name="dragFromEdge">
|
||||||
|
<flag name="left" value="1" />
|
||||||
|
<flag name="right" value="2" />
|
||||||
|
</attr>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
Loading…
Reference in a new issue