我正在尝试使用Nick Butcher's Plaid app中的示例创建一个可以在拖动时解除的活动。我确实按原样使用了他的代码,似乎添加了draggableListener
,但由于某种原因,当我拖动活动时似乎没有调用onDrag
,因此活动没有没完。
package io.traffitool.ui.widget;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
import io.traffitool.R;
import io.traffitool.util.AnimUtils;
import io.traffitool.util.ColorUtils;
import io.traffitool.util.ViewUtils;
public class ElasticDragDismissFrameLayout extends FrameLayout {
// configurable attribs
private float dragDismissDistance = Float.MAX_VALUE;
private float dragDismissFraction = -1f;
private float dragDismissScale = 1f;
private boolean shouldScale = false;
private float dragElacticity = 0.8f;
// state
private float totalDrag;
private boolean draggingDown = false;
private boolean draggingUp = false;
private List<ElasticDragDismissCallback> callbacks;
public ElasticDragDismissFrameLayout(Context context) {
this(context, null, 0, 0);
}
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0);
if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance)) {
dragDismissDistance = a.getDimensionPixelSize(R.styleable
.ElasticDragDismissFrameLayout_dragDismissDistance, 0);
} else if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction)) {
dragDismissFraction = a.getFloat(R.styleable
.ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction);
}
if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale)) {
dragDismissScale = a.getFloat(R.styleable
.ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale);
shouldScale = dragDismissScale != 1f;
}
if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragElasticity)) {
dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity,
dragElacticity);
}
a.recycle();
}
public static abstract class ElasticDragDismissCallback {
/**
* Called for each drag event.
*
* @param elasticOffset Indicating the drag offset with elasticity applied i.e. may
* exceed 1.
* @param elasticOffsetPixels The elastically scaled drag distance in pixels.
* @param rawOffset Value from [0, 1] indicating the raw drag offset i.e.
* without elasticity applied. A value of 1 indicates that the
* dismiss distance has been reached.
* @param rawOffsetPixels The raw distance the user has dragged
*/
void onDrag(float elasticOffset, float elasticOffsetPixels,
float rawOffset, float rawOffsetPixels) {
Log.d("EDDFL: ", "onDrag");
}
/**
* Called when dragging is released and has exceeded the threshold dismiss distance.
*/
void onDragDismissed() {
Log.d("EDDFL: ", "onDragDismissed");
}
}
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
// if we're in a drag gesture and the user reverses up the we should take those events
if (draggingDown && dy > 0 || draggingUp && dy < 0) {
dragScale(dy);
consumed[1] = dy;
}
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
dragScale(dyUnconsumed);
}
@Override
public void onStopNestedScroll(View child) {
if (Math.abs(totalDrag) >= dragDismissDistance) {
dispatchDismissCallback();
} else { // settle back to natural position
animate()
.translationY(0f)
.scaleX(1f)
.scaleY(1f)
.setDuration(200L)
.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getContext()))
.setListener(null)
.start();
totalDrag = 0;
draggingDown = draggingUp = false;
dispatchDragCallback(0f, 0f, 0f, 0f);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (dragDismissFraction > 0f) {
dragDismissDistance = h * dragDismissFraction;
}
}
public void addListener(ElasticDragDismissCallback listener) {
if (callbacks == null) {
callbacks = new ArrayList<>();
}
callbacks.add(listener);
}
public void removeListener(ElasticDragDismissCallback listener) {
if (callbacks != null && callbacks.size() > 0) {
callbacks.remove(listener);
}
}
private void dragScale(int scroll) {
if (scroll == 0) return;
totalDrag += scroll;
// track the direction & set the pivot point for scaling
// don't double track i.e. if start dragging down and then reverse, keep tracking as
// dragging down until they reach the 'natural' position
if (scroll < 0 && !draggingUp && !draggingDown) {
draggingDown = true;
if (shouldScale) setPivotY(getHeight());
} else if (scroll > 0 && !draggingDown && !draggingUp) {
draggingUp = true;
if (shouldScale) setPivotY(0f);
}
// how far have we dragged relative to the distance to perform a dismiss
// (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance));
// calculate the desired translation given the drag fraction
float dragTo = dragFraction * dragDismissDistance * dragElacticity;
if (draggingUp) {
// as we use the absolute magnitude when calculating the drag fraction, need to
// re-apply the drag direction
dragTo *= -1;
}
setTranslationY(dragTo);
if (shouldScale) {
final float scale = 1 - ((1 - dragDismissScale) * dragFraction);
setScaleX(scale);
setScaleY(scale);
}
// if we've reversed direction and gone past the settle point then clear the flags to
// allow the list to get the scroll events & reset any transforms
if ((draggingDown && totalDrag >= 0)
|| (draggingUp && totalDrag <= 0)) {
totalDrag = dragTo = dragFraction = 0;
draggingDown = draggingUp = false;
setTranslationY(0f);
setScaleX(1f);
setScaleY(1f);
}
dispatchDragCallback(dragFraction, dragTo,
Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag);
}
private void dispatchDragCallback(float elasticOffset, float elasticOffsetPixels,
float rawOffset, float rawOffsetPixels) {
if (callbacks != null && !callbacks.isEmpty()) {
for (ElasticDragDismissCallback callback : callbacks) {
callback.onDrag(elasticOffset, elasticOffsetPixels,
rawOffset, rawOffsetPixels);
}
}
}
private void dispatchDismissCallback() {
if (callbacks != null && !callbacks.isEmpty()) {
for (ElasticDragDismissCallback callback : callbacks) {
callback.onDragDismissed();
}
}
}
/**
* An {@link ElasticDragDismissCallback} which fades system chrome (i.e. status bar and
* navigation bar) whilst elastic drags are performed and
* {@link Activity#finishAfterTransition() finishes} the activity when drag dismissed.
*/
public static class SystemChromeFader extends ElasticDragDismissCallback {
private final Activity activity;
private final int statusBarAlpha;
private final int navBarAlpha;
private final boolean fadeNavBar;
public SystemChromeFader(Activity activity) {
this.activity = activity;
statusBarAlpha = Color.alpha(activity.getWindow().getStatusBarColor());
navBarAlpha = Color.alpha(activity.getWindow().getNavigationBarColor());
fadeNavBar = ViewUtils.isNavBarOnBottom(activity);
}
@Override
public void onDrag(float elasticOffset, float elasticOffsetPixels,
float rawOffset, float rawOffsetPixels) {
if (elasticOffsetPixels > 0) {
// dragging downward, fade the status bar in proportion
activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha(activity.getWindow()
.getStatusBarColor(), (int) ((1f - rawOffset) * statusBarAlpha)));
} else if (elasticOffsetPixels == 0) {
// reset
activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha(
activity.getWindow().getStatusBarColor(), statusBarAlpha));
activity.getWindow().setNavigationBarColor(ColorUtils.modifyAlpha(
activity.getWindow().getNavigationBarColor(), navBarAlpha));
} else if (fadeNavBar) {
// dragging upward, fade the navigation bar in proportion
activity.getWindow().setNavigationBarColor(
ColorUtils.modifyAlpha(activity.getWindow().getNavigationBarColor(),
(int) ((1f - rawOffset) * navBarAlpha)));
}
}
public void onDragDismissed() {
activity.finishAfterTransition();
}
}
}
package io.traffitool.ui;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.traffitool.R;
import io.traffitool.ui.widget.ElasticDragDismissFrameLayout;
public class ProjectActivity extends Activity {
private static final String TAG = "ProjectActivity: ";
@BindView(R.id.draggable_frame) ElasticDragDismissFrameLayout draggableFrame;
@BindView(R.id.list)RecyclerView list;
private ElasticDragDismissFrameLayout.SystemChromeFader chromeFader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_project);
ButterKnife.bind(this);
chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(this) {
@Override
public void onDragDismissed() {
Log.d(TAG, "onDragDismissed");
setResultandFinish();
}
};
draggableFrame.addListener(chromeFader);
Log.d(TAG, "draggableListenerAdded");
}
void setResultandFinish() {
Log.d(TAG, "setResultandFinish");
finish();
}
}
<?xml version="1.0" encoding="utf-8"?>
<io.traffitool.ui.widget.ElasticDragDismissFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/draggable_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionGroup="false"
app:dragDismissDistance="@dimen/drag_dismiss_distance"
app:dragDismissScale="0.95"
tools:context=".ui.ProjectActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingBottom="@dimen/padding_normal"
android:scrollbarStyle="insideOverlay"
android:scrollbars="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</io.traffitool.ui.widget.ElasticDragDismissFrameLayout>
答案 0 :(得分:0)
我遇到了与ViewPager2
类似的问题,并且深入研究了Plaid之后,我注意到RecyclerView.Adapter
中每个项目的布局都有一个ScrollView
和android:nestedScrollingEnabled="true"
,例如about_icon.xml。
将每个布局包装在NestedScrollView
中启用的ElasticDragDismissFrameLayout
中的嵌套滚动事件中。
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<!-- Content -->
</androidx.core.widget.NestedScrollView>