看下整體實現效果,側邊滑出一個菜單選項。
首先,我們需要實現從右邊滑出菜單選項的背景色,利用二階貝塞爾曲線即可實現:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class MenuBgView extends View {
Paint paint;
Path path;
public MenuBgView(Context context) {
this(context, null);
}
public MenuBgView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MenuBgView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
path = new Path();
}
public void setTouchY(float y, float percent) {
path.reset();
float width = getWidth() * percent;
float offsetY = getHeight() / 9;
float beginX = 0;
float beginY = -offsetY;
float endX = 0;
float endY = getHeight() + offsetY;
float controllX = width * 3 / 2;
float controllY = y;
path.lineTo(beginX, beginY);
path.quadTo(controllX, controllY, endX, endY);
path.close();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
}
public void setColor(Drawable color) {
if (color instanceof ColorDrawable) {
ColorDrawable colorDrawable= (ColorDrawable) color;
paint.setColor(colorDrawable.getColor());
}
}
}
在MainActivity中使用:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
public class MainActivity extends AppCompatActivity {
MenuBgView menuBgView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
menuBgView = new MenuBgView(this);
setContentView(menuBgView);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
menuBgView.setTouchY(event.getY(),0.8f);
return super.onTouchEvent(event);
}
}
完成後效果如圖,這個就是我們菜單欄的背景view。
下面完成菜單view裏面的文字內容的view,這裏叫MenuContentLayout:
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
public class MenuContentLayout extends LinearLayout {
private float maxTranslationX;
public MenuContentLayout(Context context) {
this(context, null);
}
public MenuContentLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MenuContentLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
setOrientation(VERTICAL);
if (attrs != null) {
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
maxTranslationX = array.getDimension(R.styleable.SideBar_maxTranslationX, 0);
array.recycle();
}
}
public void setTouchY(float y, float slideOffset) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
apply(child, y, slideOffset);
}
}
private void apply(View child, float y, float slideOffset) {
float translationX = 0;
int centerY = child.getTop() + child.getHeight() / 2;
float distance = Math.abs(y - centerY);
float scale = distance / getHeight() * 3;
translationX = maxTranslationX - scale * maxTranslationX;
child.setTranslationX(translationX);
}
}
上面這個view也很簡單,就是遍歷整個MenuContentLayout裏面的子view,拿出來一個一個動態的設置他們的位置,這裏主要是如何根據手指的上下移動,來確定和哪個內容子view最近,從而將該子view重新設置它的橫軸位置,達到動態的左右移動。
下面這個MenuPutLayout就是將上面兩個自定義view進行整合的layout,負責傳遞onTouchEvent事件,以及設置兩個子view:
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class MenuPutLayout extends RelativeLayout {
MenuContentLayout contentLayout;
MenuBgView bgView;
public MenuPutLayout(MenuContentLayout contentLayout) {
this(contentLayout.getContext());
init(contentLayout);
}
public MenuPutLayout(Context context) {
this(context, null);
}
public MenuPutLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MenuPutLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(MenuContentLayout contentLayout) {
this.contentLayout = contentLayout;
//把content的 寬高轉移到外面RelatiLayout
setLayoutParams(contentLayout.getLayoutParams());
//背景先添加進去
bgView = new MenuBgView(getContext());
bgView.setColor(contentLayout.getBackground());
addView(bgView, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
//把contentView 的背景顏色取出來 設置給 bgView 把contentView弄成透明
contentLayout.setBackgroundColor(Color.TRANSPARENT);
addView(contentLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
/**
* 傳遞偏移Y
* @param y
* @param slideOffset
*/
public void setTouchY(float y, float slideOffset) {
bgView.setTouchY(y,slideOffset);
contentLayout.setTouchY(y, slideOffset);
}
}
最後就是MenuDrawerLayout,這裏利用DrawerLayout的滑動回調的參數,往MenuPutLayout中傳遞參數:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
public class MenuDrawerLayout extends DrawerLayout implements DrawerLayout.DrawerListener {
private MenuContentLayout menuContentLayout;
private View contentView;
private MenuPutLayout menuPutLayout;
private float y;
private float slideOffset;
public MenuDrawerLayout(@NonNull Context context) {
super(context);
}
public MenuDrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MenuDrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
init();
}
private void init() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (view instanceof MenuContentLayout) {
menuContentLayout = (MenuContentLayout) view;
} else {
contentView = view;
}
}
removeView(menuContentLayout);
menuPutLayout = new MenuPutLayout(menuContentLayout);
addView(menuPutLayout);
addDrawerListener(this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
y = ev.getY();
//沒有打開之前 不攔截 打開之後攔不攔截 大於1 後 內容區域不再進行偏移
// if (slideOffset < 0.8) {
// return super.dispatchTouchEvent(ev);
// } else {
// //等於 1
// menuPutLayout.setTouchY(y, slideOffset);
// }
return super.dispatchTouchEvent(ev);
}
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
this.slideOffset = slideOffset;
menuPutLayout.setTouchY(y, slideOffset);
//針對內容區域進行破偏移
float contentViewOffset = drawerView.getWidth() * slideOffset / 2;
contentView.setTranslationX(contentViewOffset);
}
@Override
public void onDrawerOpened(@NonNull View drawerView) {
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, GravityCompat.END);
}
@Override
public void onDrawerClosed(@NonNull View drawerView) {
}
@Override
public void onDrawerStateChanged(int newState) {
}
}
整個項目的完整代碼詳見:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn18