我的问题是为什么当我拖动CardView
时看不到任何动画。在许多示例中,存在动画。也许有人也知道为什么当我从右向左滑动时会看到从左向右的动画。
布局:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipe.card"
android:layout_width="match_parent"
android:layout_height="200dp"
card_view:cardCornerRadius="4dp"
card_view:contentPadding="8dp"
android:layout_margin="16dp"
app:layout_anchorGravity="bottom">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Card"
android:lineSpacingExtra="6dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SWIPE THIS CARD"
android:layout_gravity="bottom|right"
android:textAllCaps="true"
android:fontFamily="sans-serif-medium"/>
</android.support.v7.widget.CardView>
</android.support.design.widget.CoordinatorLayout>
活动:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_swipe)
val swipe = SwipeDismissBehavior<CardView>()
swipe.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_ANY)
val cardView = findViewById<View>(R.id.swipe_card) as CardView
val coordinatorParams = cardView.layoutParams as CoordinatorLayout.LayoutParams
coordinatorParams.behavior = swipe
}
}
支持库版本:
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
答案 0 :(得分:0)
尝试将此行添加到cardView:
cardView.setOnTouchListener { v, event ->
if (v != null && v is CardView) {
b.onInterceptTouchEvent(coordinatorLayout, v, event)
} else {
false
}
}
但是,该卡仅在滑动手势后显示滑动动画。如果您玩卡并向左和向右滑动,将什么也不会发生。
然后我检查Snackbar如何实现此功能,但它们是相同的。新的小吃栏仅响应快速滑动。我记得我们以前可以玩小吃店,但现在不能。我想这是出于一些新的材料设计考虑。
答案 1 :(得分:0)
我认为支持库存在问题,因此我复制了代码并对其进行了一些修改。这是工作示例:
将新行为应用于snackbar
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onShown(Snackbar sb) {
super.onShown(sb);
ViewGroup.LayoutParams params = sb.getView().getLayoutParams();
if (params instanceof CoordinatorLayout.LayoutParams) {
SwipeDismissBehavior2<View> behavior = new SwipeDismissBehavior2<>();
behavior.setSwipeDirection(SwipeDismissBehavior2.SWIPE_DIRECTION_ANY);
((CoordinatorLayout.LayoutParams) params).setBehavior(behavior);
}
}
});
新行为:
public class SwipeDismissBehavior2<V extends View> extends Behavior<V> {
/**
* A view is not currently being dragged or animating as a result of a fling/snap.
*/
public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
/**
* A view is currently being dragged. The position is currently changing as a result
* of user input or simulated user input.
*/
public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
/**
* A view is currently settling into place as a result of a fling or
* predefined non-interactive motion.
*/
public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
/** @hide */
@IntDef({SWIPE_DIRECTION_START_TO_END, SWIPE_DIRECTION_END_TO_START, SWIPE_DIRECTION_ANY})
@Retention(RetentionPolicy.SOURCE)
private @interface SwipeDirection {}
/**
* Swipe direction that only allows swiping in the direction of start-to-end. That is
* left-to-right in LTR, or right-to-left in RTL.
*/
public static final int SWIPE_DIRECTION_START_TO_END = 0;
/**
* Swipe direction that only allows swiping in the direction of end-to-start. That is
* right-to-left in LTR or left-to-right in RTL.
*/
public static final int SWIPE_DIRECTION_END_TO_START = 1;
/**
* Swipe direction which allows swiping in either direction.
*/
public static final int SWIPE_DIRECTION_ANY = 2;
private static final float DEFAULT_DRAG_DISMISS_THRESHOLD = 0.5f;
private static final float DEFAULT_ALPHA_START_DISTANCE = 0f;
private static final float DEFAULT_ALPHA_END_DISTANCE = DEFAULT_DRAG_DISMISS_THRESHOLD;
private ViewDragHelper mViewDragHelper;
private OnDismissListener mListener;
private boolean mIgnoreEvents;
private float mSensitivity = 0f;
private boolean mSensitivitySet;
private int mSwipeDirection = SWIPE_DIRECTION_ANY;
private float mDragDismissThreshold = DEFAULT_DRAG_DISMISS_THRESHOLD;
private float mAlphaStartSwipeDistance = DEFAULT_ALPHA_START_DISTANCE;
private float mAlphaEndSwipeDistance = DEFAULT_ALPHA_END_DISTANCE;
/**
* Callback interface used to notify the application that the view has been dismissed.
*/
public interface OnDismissListener {
/**
* Called when {@code view} has been dismissed via swiping.
*/
public void onDismiss(View view);
/**
* Called when the drag state has changed.
*
* @param state the new state. One of
* {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
*/
public void onDragStateChanged(int state);
}
/**
* Set the listener to be used when a dismiss event occurs.
*
* @param listener the listener to use.
*/
public void setListener(OnDismissListener listener) {
mListener = listener;
}
/**
* Sets the swipe direction for this behavior.
*
* @param direction one of the {@link #SWIPE_DIRECTION_START_TO_END},
* {@link #SWIPE_DIRECTION_END_TO_START} or {@link #SWIPE_DIRECTION_ANY}
*/
public void setSwipeDirection(@SwipeDirection int direction) {
mSwipeDirection = direction;
}
/**
* Set the threshold for telling if a view has been dragged enough to be dismissed.
*
* @param distance a ratio of a view's width, values are clamped to 0 >= x <= 1f;
*/
public void setDragDismissDistance(float distance) {
mDragDismissThreshold = clamp(0f, distance, 1f);
}
/**
* The minimum swipe distance before the view's alpha is modified.
*
* @param fraction the distance as a fraction of the view's width.
*/
public void setStartAlphaSwipeDistance(float fraction) {
mAlphaStartSwipeDistance = clamp(0f, fraction, 1f);
}
/**
* The maximum swipe distance for the view's alpha is modified.
*
* @param fraction the distance as a fraction of the view's width.
*/
public void setEndAlphaSwipeDistance(float fraction) {
mAlphaEndSwipeDistance = clamp(0f, fraction, 1f);
}
/**
* Set the sensitivity used for detecting the start of a swipe. This only takes effect if
* no touch handling has occured yet.
*
* @param sensitivity Multiplier for how sensitive we should be about detecting
* the start of a drag. Larger values are more sensitive. 1.0f is normal.
*/
public void setSensitivity(float sensitivity) {
mSensitivity = sensitivity;
mSensitivitySet = true;
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Reset the ignore flag
if (mIgnoreEvents) {
mIgnoreEvents = false;
return false;
}
break;
default:
mIgnoreEvents = !parent.isPointInChildBounds(child,
(int) event.getX(), (int) event.getY());
break;
}
if (mIgnoreEvents) {
return false;
}
ensureViewDragHelper(parent);
return mViewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
if (mViewDragHelper != null) {
mViewDragHelper.processTouchEvent(event);
return true;
}
return false;
}
private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
private int mOriginalCapturedViewLeft;
@Override
public boolean tryCaptureView(View child, int pointerId) {
mOriginalCapturedViewLeft = child.getLeft();
return true;
}
@Override
public void onViewDragStateChanged(int state) {
if (mListener != null) {
mListener.onDragStateChanged(state);
}
}
@Override
public void onViewReleased(View child, float xvel, float yvel) {
final int childWidth = child.getWidth();
int targetLeft;
boolean dismiss = false;
if (shouldDismiss(child, xvel)) {
targetLeft = child.getLeft() < mOriginalCapturedViewLeft
? mOriginalCapturedViewLeft - childWidth
: mOriginalCapturedViewLeft + childWidth;
dismiss = true;
} else {
// Else, reset back to the original left
targetLeft = mOriginalCapturedViewLeft;
}
if (mViewDragHelper.settleCapturedViewAt(targetLeft, child.getTop())) {
ViewCompat.postOnAnimation(child, new SettleRunnable(child, dismiss));
} else if (dismiss) {
child.setVisibility(View.GONE);
if (mListener != null) {
mListener.onDismiss(child);
}
}
}
private boolean shouldDismiss(View child, float xvel) {
if (xvel != 0f) {
final boolean isRtl = ViewCompat.getLayoutDirection(child)
== ViewCompat.LAYOUT_DIRECTION_RTL;
if (mSwipeDirection == SWIPE_DIRECTION_ANY) {
// We don't care about the direction so return true
return true;
} else if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
// We only allow start-to-end swiping, so the fling needs to be in the
// correct direction
return isRtl ? xvel < 0f : xvel > 0f;
} else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
// We only allow end-to-start swiping, so the fling needs to be in the
// correct direction
return isRtl ? xvel > 0f : xvel < 0f;
}
} else {
final int distance = child.getLeft() - mOriginalCapturedViewLeft;
final int thresholdDistance = Math.round(child.getWidth() * mDragDismissThreshold);
return Math.abs(distance) >= thresholdDistance;
}
return false;
}
@Override
public int getViewHorizontalDragRange(View child) {
return child.getWidth();
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final boolean isRtl = ViewCompat.getLayoutDirection(child)
== ViewCompat.LAYOUT_DIRECTION_RTL;
int min, max;
if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) {
if (isRtl) {
min = mOriginalCapturedViewLeft - child.getWidth();
max = mOriginalCapturedViewLeft;
} else {
min = mOriginalCapturedViewLeft;
max = mOriginalCapturedViewLeft + child.getWidth();
}
} else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) {
if (isRtl) {
min = mOriginalCapturedViewLeft;
max = mOriginalCapturedViewLeft + child.getWidth();
} else {
min = mOriginalCapturedViewLeft - child.getWidth();
max = mOriginalCapturedViewLeft;
}
} else {
min = mOriginalCapturedViewLeft - child.getWidth();
max = mOriginalCapturedViewLeft + child.getWidth();
}
return clamp(min, left, max);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return child.getTop();
}
@Override
public void onViewPositionChanged(View child, int left, int top, int dx, int dy) {
final float distance = fraction(0, child.getWidth(), Math.abs(left - mOriginalCapturedViewLeft));
child.setAlpha(clamp(0f, 1f - distance, 1f));
}
};
private void ensureViewDragHelper(ViewGroup parent) {
if (mViewDragHelper == null) {
mViewDragHelper = mSensitivitySet
? ViewDragHelper.create(parent, mSensitivity, mDragCallback)
: ViewDragHelper.create(parent, mDragCallback);
}
}
private class SettleRunnable implements Runnable {
private final View mView;
private final boolean mDismiss;
SettleRunnable(View view, boolean dismiss) {
mView = view;
mDismiss = dismiss;
}
@Override
public void run() {
if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
ViewCompat.postOnAnimation(mView, this);
} else {
if (mDismiss) {
mView.setVisibility(View.GONE);
if (mListener != null) {
mListener.onDismiss(mView);
}
}
}
}
}
private static float clamp(float min, float value, float max) {
return Math.min(Math.max(min, value), max);
}
private static int clamp(int min, int value, int max) {
return Math.min(Math.max(min, value), max);
}
/**
* Retrieve the current drag state of this behavior. This will return one of
* {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
*
* @return The current drag state
*/
public int getDragState() {
return mViewDragHelper != null ? mViewDragHelper.getViewDragState() : STATE_IDLE;
}
/**
* The fraction that {@code value} is between {@code startValue} and {@code endValue}.
*/
static float fraction(float startValue, float endValue, float value) {
return (value - startValue) / (endValue - startValue);
}
}