效果如图:
代码如下:
public class SlidingUpLayout extends ViewGroup {
private View mBelowView;
private View mDragView;
private View mAboveView;
private ViewDragHelper mViewDragHelper;
private float mAlpha;
private float mSlideOffset;
private float mAnchorPointer;
private State mState = State.ANCHORED;
private int mSettlePosition;
public enum State {
EXPANDED,
COLLAPSED,
ANCHORED
}
private OnStateChangedListener onStateChangedListener;
public SlidingUpLayout(Context context) {
this(context, null);
}
public SlidingUpLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingUpLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpLayout);
mAnchorPointer = mTypedArray.getFloat(R.styleable.SlidingUpLayout_anchorPointer, 0.6f);
mTypedArray.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBelowView = getChildAt(0);
mDragView = getChildAt(1);
mAboveView = getChildAt(2);
mViewDragHelper = ViewDragHelper.create(this, new SlidingUpViewDragHelper());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int belowHeight = MeasureSpec.makeMeasureSpec(measureHeight, MeasureSpec.EXACTLY);
mBelowView.measure(widthMeasureSpec, belowHeight);
LayoutParams dragViewLayoutParams = mDragView.getLayoutParams();
int dragViewWidth = MeasureSpec.makeMeasureSpec(dragViewLayoutParams.width, MeasureSpec.EXACTLY);
int dragViewHeight = MeasureSpec.makeMeasureSpec(dragViewLayoutParams.height, MeasureSpec.EXACTLY);
mDragView.measure(dragViewWidth, dragViewHeight);
mAboveView.measure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
switch (mState) {
case EXPANDED:
mAlpha = (float) 0.5;
mSlideOffset = 1;
break;
case COLLAPSED:
mAlpha = 0;
mSlideOffset = 0;
break;
case ANCHORED:
mAlpha = (float) (mAnchorPointer * 0.5);
mSlideOffset = mAnchorPointer;
break;
}
int belowViewWidth = mBelowView.getMeasuredWidth();
int belowViewHeight = mBelowView.getMeasuredHeight();
mBelowView.layout(0, 0, belowViewWidth, belowViewHeight);
int dragViewWidth = mDragView.getMeasuredWidth();
int dragViewHeight = mDragView.getMeasuredHeight();
mDragView.layout(belowViewWidth / 2 - dragViewWidth / 2,
(int) (belowViewHeight - dragViewHeight - mSlideOffset * (belowViewHeight - dragViewHeight)),
belowViewWidth / 2 + dragViewWidth / 2,
(int) (belowViewHeight - mSlideOffset * (belowViewHeight - dragViewHeight)));
int aboveViewWidth = mAboveView.getMeasuredWidth();
int aboveViewHeight = mAboveView.getMeasuredHeight();
mAboveView.layout(0,
(int) (belowViewHeight - mSlideOffset * (belowViewHeight - dragViewHeight)),
aboveViewWidth,
(int) (belowViewHeight + aboveViewHeight - mSlideOffset * (belowViewHeight - dragViewHeight)));
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean result;
@SuppressLint("WrongConstant") final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
if (child == mBelowView) {
Rect mTmpRect = new Rect();
canvas.getClipBounds(mTmpRect);
mTmpRect.bottom = mAboveView.getTop();
canvas.clipRect(mTmpRect);
result = super.drawChild(canvas, child, drawingTime);
Paint mCoveredFadePaint = new Paint();
final int baseAlpha = 0xff000000 >>> 24;
final int imag = (int) (baseAlpha * mAlpha);
final int color = imag << 24;
mCoveredFadePaint.setColor(color);
canvas.drawRect(mTmpRect, mCoveredFadePaint);
} else {
result = super.drawChild(canvas, child, drawingTime);
}
canvas.restoreToCount(save);
return result;
}
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
invalidate();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mViewDragHelper.processTouchEvent(ev);
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
class SlidingUpViewDragHelper extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mDragView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == mDragView) {
return mBelowView.getWidth() / 2 - mDragView.getWidth() / 2;
}
return super.clampViewPositionHorizontal(child, left, dx);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (top > mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight()) {
mSettlePosition = mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight();
return mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight();
} else if (top < 0) {
mSettlePosition = 0;
return 0;
} else {
mSettlePosition = top - mDragView.getMeasuredHeight();
mAlpha = (float) ((mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight() - top) * 0.5 / (mBelowView.getMeasuredHeight()
- mDragView.getMeasuredHeight()));
}
return top;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if (changedView == mDragView) {
mAboveView.layout(0,
top + mDragView.getMeasuredHeight(),
mAboveView.getMeasuredWidth(),
mAboveView.getMeasuredHeight() + top + mDragView.getMeasuredHeight());
}
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (mSettlePosition > mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight() / 2 - mDragView.getMeasuredHeight()) {
mDragView.layout(mBelowView.getMeasuredWidth() / 2 - mDragView.getMeasuredWidth() / 2,
mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight(),
mBelowView.getMeasuredWidth() / 2 + mDragView.getMeasuredWidth() / 2,
mBelowView.getMeasuredHeight());
mAboveView.layout(0,
mBelowView.getMeasuredHeight(),
mAboveView.getMeasuredWidth(),
mBelowView.getMeasuredHeight() + mAboveView.getMeasuredHeight());
mState = State.COLLAPSED;
} else if (mSettlePosition <= mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight() / 2 - mDragView.getMeasuredHeight()
&& mSettlePosition > mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight() * 3 / 2 - mDragView.getMeasuredHeight()) {
mDragView.layout(mBelowView.getMeasuredWidth() / 2 - mDragView.getMeasuredWidth() / 2,
(int) (mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight() - mDragView.getMeasuredHeight()),
mBelowView.getMeasuredWidth() / 2 + mDragView.getMeasuredWidth() / 2,
(int) (mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight()));
mAboveView.layout(0,
(int) (mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight()),
mAboveView.getMeasuredWidth(),
(int) (mBelowView.getMeasuredHeight() - mAnchorPointer * mBelowView.getMeasuredHeight() + mAboveView.getMeasuredHeight()));
mState = State.ANCHORED;
} else {
mDragView.layout(mBelowView.getMeasuredWidth() / 2 - mDragView.getMeasuredWidth() / 2,
0,
mBelowView.getMeasuredWidth() / 2 + mDragView.getMeasuredWidth() / 2,
mDragView.getMeasuredHeight());
mAboveView.layout(0,
mDragView.getMeasuredHeight(),
mAboveView.getMeasuredWidth(),
mDragView.getMeasuredHeight() + mAboveView.getMeasuredHeight());
mState = State.EXPANDED;
}
switch (mState) {
case EXPANDED:
mAlpha = (float) 0.5;
break;
case COLLAPSED:
mAlpha = 0;
break;
case ANCHORED:
mAlpha = (float) (mAnchorPointer * 0.5);
break;
}
super.onViewReleased(releasedChild, xvel, yvel);
}
}
public void setAnchorPointer(float anchorPointer) {
this.mAnchorPointer = anchorPointer;
postInvalidate();
}
public void setState(State state) {
this.mState = state;
postInvalidate();
}
public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
this.onStateChangedListener = onStateChangedListener;
}
public interface OnStateChangedListener {
public void onStateChanged(State state);
}
}
注意:需要依赖V4包
implementation 'com.android.support:support-v4:26.0.1'
attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SlidingUpLayout">
<attr name="anchorPointer" format="float"/>
</declare-styleable>
</resources>
布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.example.a1qu212.myapplication.SlidingUpLayout 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_height="match_parent"
app:anchorPointer="0.44">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_dark"/>
<RelativeLayout
android:layout_width="100dp"
android:layout_height="50dp"
android:background="@android:color/holo_green_dark"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
</com.example.a1qu212.myapplication.SlidingUpLayout>
存在的问题:
计算不够准确;
参考https://github.com/umano/AndroidSlidingUpPanel