Android自定義側滑菜單選項

看下整體實現效果,側邊滑出一個菜單選項。

完整代碼詳見

首先,我們需要實現從右邊滑出菜單選項的背景色,利用二階貝塞爾曲線即可實現:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章