最近发现很多app都使用了三段式滑动,比如说高德的首页和某宝等物流信息都是使用的三段式滑动方式,谷歌其实给了我们很好的2段式滑动,就是BottomSheet,所以这次我也是在这个原理基础上做了一个小小的修改来实现我们今天想要的效果。
高德的效果
gaode_300.gif
实现的效果
fgaode_300.gif
我们实现的效果和高德差距不是很大,也很顺滑。具体实现其实就是集成CoordinatorLayout.Behavior
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.zwl.mybehaviordemo.gaode;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build.VERSION;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.VisibleForTesting;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.coordinatorlayout.widget.CoordinatorLayout.Behavior;
import androidx.core.math.MathUtils;
import androidx.core.view.ViewCompat;
import androidx.customview.view.AbsSavedState;
import androidx.customview.widget.ViewDragHelper;
import androidx.customview.widget.ViewDragHelper.Callback;
import com.zwl.mybehaviordemo.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* 高德首页滑动效果
*
* @author yixiaolunhui
*/
public class GaoDeBottomSheetBehavior extends CoordinatorLayout.Behavior {
public static final int STATE_DRAGGING = 1;
public static final int STATE_SETTLING = 2;
public static final int STATE_EXPANDED = 3;
public static final int STATE_COLLAPSED = 4;
public static final int STATE_HIDDEN = 5;
public static final int STATE_HALF_EXPANDED = 6;
public static final int PEEK_HEIGHT_AUTO = -1;
private static final float HIDE_THRESHOLD = 0.5F;
private static final float HIDE_FRICTION = 0.1F;
public static final int MIDDLE_HEIGHT_AUTO = -1;
private boolean fitToContents = true;
private float maximumVelocity;
private int peekHeight;
private boolean peekHeightAuto;
private int peekHeightMin;
private int lastPeekHeight;
int fitToContentsOffset;
int halfExpandedOffset;
int collapsedOffset;
boolean hideable;
private boolean skipCollapsed;
int state = STATE_COLLAPSED;
ViewDragHelper viewDragHelper;
private boolean ignoreEvents;
private int lastNestedScrollDy;
private boolean nestedScrolled;
int parentHeight;
WeakReference viewRef;
WeakReference nestedScrollingChildRef;
private GaoDeBottomSheetBehavior.BottomSheetCallback callback;
private VelocityTracker velocityTracker;
int activePointerId;
private int initialY;
boolean touchingScrollingChild;
private Map importantForAccessibilityMap;
private final Callback dragCallback;
private int mMiddleHeight;
private boolean mMiddleHeightAuto;
public GaoDeBottomSheetBehavior() {
this.dragCallback = new NamelessClass_1();
}
public GaoDeBottomSheetBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
this.dragCallback = new NamelessClass_1();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BottomSheetBehavior_Layout);
TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight);
if (value != null && value.data == -1) {
this.setPeekHeight(value.data);
} else {
this.setPeekHeight(a.getDimensionPixelSize(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, -1));
}
this.setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_hideAble, false));
this.setFitToContents(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_fitToContents, true));
this.setSkipCollapsed(a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapse, false));
setMiddleHeight(a.getDimensionPixelSize(R.styleable.BottomSheetBehavior_Layout_behavior_middleHeight, MIDDLE_HEIGHT_AUTO));
a.recycle();
ViewConfiguration configuration = ViewConfiguration.get(context);
this.maximumVelocity = (float) configuration.getScaledMaximumFlingVelocity();
}
class NamelessClass_1 extends Callback {
NamelessClass_1() {
}
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
if (GaoDeBottomSheetBehavior.this.state == STATE_DRAGGING) {
return false;
} else if (GaoDeBottomSheetBehavior.this.touchingScrollingChild) {
return false;
} else {
if (GaoDeBottomSheetBehavior.this.state == 3 && GaoDeBottomSheetBehavior.this.activePointerId == pointerId) {
View scroll = (View) GaoDeBottomSheetBehavior.this.nestedScrollingChildRef.get();
if (scroll != null && scroll.canScrollVertically(-1)) {
return false;
}
}
return GaoDeBottomSheetBehavior.this.viewRef != null && GaoDeBottomSheetBehavior.this.viewRef.get() == child;
}
}
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
GaoDeBottomSheetBehavior.this.dispatchOnSlide(top);
}
@Override
public void onViewDragStateChanged(int state) {
if (state == 1) {
GaoDeBottomSheetBehavior.this.setStateInternal(STATE_DRAGGING);
}
}
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
int top;
byte targetState;
int currentTop;
if (yvel < 0.0F) {
if (GaoDeBottomSheetBehavior.this.fitToContents) {
currentTop = releasedChild.getTop();
if (currentTop < (collapsedOffset + HIDE_THRESHOLD) && currentTop >= halfExpandedOffset) {
top = GaoDeBottomSheetBehavior.this.halfExpandedOffset;
targetState = STATE_HALF_EXPANDED;
} else {
top = GaoDeBottomSheetBehavior.this.fitToContentsOffset;
targetState = STATE_EXPANDED;
}
} else {
currentTop = releasedChild.getTop();
if (currentTop > GaoDeBottomSheetBehavior.this.halfExpandedOffset) {
top = GaoDeBottomSheetBehavior.this.halfExpandedOffset;
targetState = STATE_HALF_EXPANDED;
} else {
top = 0;
targetState = STATE_EXPANDED;
}
}
} else if (!GaoDeBottomSheetBehavior.this.hideable || !GaoDeBottomSheetBehavior.this.shouldHide