在supportV4中有一个侧滑的View叫做DrawerLayout,支持左右滑动。最近项目中刚好有一个下向上滑动的组件需求,所以参考了一个改实现,打造了一个上下滑动的组件,发现代码简易,值得分享出来供大家参考。
需求功能分析
需求简述:实现一个滑动底部滑块,展开的容器控件,滑块自身内容自行定制。上滑动展开,下滑动关闭,点击开关进行对应的展开关闭。关键点:
- 控件支持内容定制
- 上下滑动进行对应的关闭、展开
需求实现
根据需求,我们可以简易列出实现过程中的几个关键点(辅助如图)
1、ViewGroup内部进行测量子控件;
2、外部提供一个关闭后的展开高度
3、提供开启关闭的回调
4、提供绑定滑动的区域
编码
package com.owant.space.view;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.owant.space.R;
/**
* created by Kyle.Zhong on 2020-04-07
*/
public class DrawerVerticalLayout extends FrameLayout {
public interface LayoutOpenCloseListener {
void onClosed();
void onOpened();
}
private View shadowView;
private ViewGroup slipBoxView;
private View touchBar;
private int closeHeight;
private float slipBoxViewTransY = 0;
public int height;
public int minTop;
public int maxTop;
private boolean isClose = true;
private LayoutOpenCloseListener layoutOpenCloseListener;
public DrawerVerticalLayout(Context context) {
this(context, null);
}
public DrawerVerticalLayout(Context context,
@Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawerVerticalLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context
.obtainStyledAttributes(attrs, R.styleable.DrawerVerticalLayout);
closeHeight = typedArray
.getDimensionPixelSize(R.styleable.DrawerVerticalLayout_closeHeight, 200);
typedArray.recycle();
}
public void setLayoutOpenCloseListener(
LayoutOpenCloseListener layoutOpenCloseListener) {
this.layoutOpenCloseListener = layoutOpenCloseListener;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
if (childCount != 2) {
throw new IllegalStateException("DrawerVerticalLayout必须添加两个子View");
}
shadowView = getChildAt(0);
shadowView.setVisibility(INVISIBLE);
slipBoxView = (ViewGroup) getChildAt(1);
touchBar = findViewById(R.id.drawer_touch_bar);
if (touchBar == null) {
throw new IllegalStateException(
"DrawerVerticalLayout必须添加一个为R.id.drawer_touch_bar的拖动View");
}
touchBar.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return onTouchBar(v, event);
}
});
}
float currentY;
float startTouchY;
public boolean isClose() {
return isClose;
}
public void setClose(boolean close) {
if (close == isClose) {
return;
}
if (close) {
boxClose();
} else {
boxOpen();
}
isClose = close;
}
private boolean onTouchBar(View v, MotionEvent event) {
int action = event.getAction();
float rawY = event.getRawY();
switch (action) {
case MotionEvent.ACTION_DOWN:
currentY = rawY;
slipBoxViewTransY = slipBoxView.getTranslationY();
startTouchY = rawY;
break;
case MotionEvent.ACTION_MOVE:
shadowView.setVisibility(VISIBLE);
float dY = rawY - currentY;
float resultY = slipBoxViewTransY + dY;
if (resultY <= minTop) {
resultY = minTop;
}
if (resultY >= maxTop) {
resultY = maxTop;
}
slipBoxView.setTranslationY(resultY);
break;
case MotionEvent.ACTION_UP:
currentY = rawY;
if (currentY > startTouchY) {
isClose = true;
boxClose();
} else {
isClose = false;
boxOpen();
}
break;
default:
break;
}
return true;
}
public void boxOpen() {
ObjectAnimator openAnimator = ObjectAnimator
.ofFloat(slipBoxView, "translationY", slipBoxViewTransY, minTop);
long time = (long) ((slipBoxViewTransY - minTop) / ((maxTop - minTop) * 1.0) * 400L);
openAnimator.setDuration(time);
openAnimator.start();
shadowView.setVisibility(VISIBLE);
if (layoutOpenCloseListener != null) {
layoutOpenCloseListener.onOpened();
}
}
public void boxClose() {
ObjectAnimator closeAnimator = ObjectAnimator
.ofFloat(slipBoxView, "translationY", slipBoxViewTransY, maxTop);
long time = (long) ((maxTop - slipBoxViewTransY) / ((maxTop - minTop) * 1.0) * 400L);
closeAnimator.setDuration(time);
closeAnimator.start();
shadowView.setVisibility(INVISIBLE);
if (layoutOpenCloseListener != null) {
layoutOpenCloseListener.onClosed();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int boxViewHeight = slipBoxView.getMeasuredHeight();
height = getMeasuredHeight();
slipBoxViewTransY = 0;
minTop = 0;
maxTop = boxViewHeight - closeHeight;
FrameLayout.LayoutParams layoutParams = (LayoutParams) slipBoxView.getLayoutParams();
layoutParams.gravity = Gravity.BOTTOM;
slipBoxView.setLayoutParams(layoutParams);
if (isClose) {
slipBoxView.setTranslationY(maxTop);
} else {
shadowView.setVisibility(VISIBLE);
}
}
}
测试布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
>
<com.owant.space.view.DrawerVerticalLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
app:closeHeight="200dp"
app:openMarginTop="20dp"
>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ff0f0f"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="600dp"
android:orientation="vertical"
>
<Button
android:id="@+id/drawer_touch_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hallo"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hallo"
/>
</LinearLayout>
</com.owant.space.view.DrawerVerticalLayout>
</LinearLayout>
结论
主要使用了ObjectAnimator进行滑动,采用Id进行绑定滑动区域。使用起来便捷爽快。