Path實現常見toolbar點選彈出選單效果

Jooyer發表於2017-03-19

今天我接著站在了巨人的肩膀上了!因為上次我參考大神利用 Path 實現了點九圖效果,附上我上次講解連結: juejin.im/post/58c3b8… .這一次,我將完成上次說的實現點選toolbar按鈕彈出選單效果,當然還是利用 Path 來完成這個效果.如果想看巨人的鉅作,請點選: juejin.im/post/5865f4… ,我此次僅僅是將大神的點九圖更換為一個由Path完成的效果而已.

首先,我們還是看下我完成的結果吧!

Path實現常見toolbar點選彈出選單效果

嘿嘿,和大神的一樣哈!是的,我只是改了背景圖而已.這樣我們就可以隨便刷不要UI哥幫忙了,而且那個三角凸起可以 左右位置調整 哦!

首先還是看下MainActivity的佈局吧

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jooyerbubblemenu.MainActivity">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:text="@string/app_name"
            android:textColor="@android:color/white"
            android:textSize="20sp"
            android:layout_centerVertical="true"/>
        <ImageView
            android:id="@+id/more"
            android:layout_width="50dp"
            android:layout_height="match_parent"
            android:src="@mipmap/common_forward_normal"
            android:scaleType="center"
            android:layout_alignParentRight="true"/>
    </RelativeLayout>
</LinearLayout>複製程式碼

這個就不說啥了,接著,來重點了,看 Recyclerview 了,因為大神用的是這個控制元件,那我只是給這個控制元件換了個衣服!

package com.jooyerbubblemenu;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;

/**
 * Created by Jooyer on 2017/3/19
 */

public class JooyerBubbleRecyclerView extends RecyclerView{

    private JooyerBubbleDrawable mBubbleDrawable;
    private float mArrowWidth;
    private float mArrowHeight;
    private float mRadius;
    private float mArrowOffset;
    private int mBubbleColor;
    private JooyerBubbleDrawable.ArrowDirection mArrowDirection;
    private boolean mArrowCenter;

    public JooyerBubbleRecyclerView(Context context) {
        this(context,null);
    }

    public JooyerBubbleRecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public JooyerBubbleRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context,attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(R.styleable.JooyerBubbleRecyclerView);
        mArrowWidth = array.getDimension(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_width,
                JooyerBubbleDrawable.Builder.DEFAULT_ARROW_WIDTH);
        mArrowHeight = array.getDimension(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_height,
                JooyerBubbleDrawable.Builder.DEFAULT_ARROW_HEIGHT);
        mRadius = array.getDimension(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_radius,
                JooyerBubbleDrawable.Builder.DEFAULT_RADIUS);
        int location = array.getInt(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_direction, 0);
        mArrowDirection = JooyerBubbleDrawable.ArrowDirection.mapIntToValue(location);
        mArrowOffset = array.getDimension(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_offset,
                JooyerBubbleDrawable.Builder.DEFAULT_ARROW_OFFSET);
        mBubbleColor = array.getColor(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_color,
                JooyerBubbleDrawable.Builder.DEFAULT_BUBBLE_COLOR);
        mArrowCenter = array.getBoolean(R.styleable.JooyerBubbleRecyclerView_jooyer_bubble_arrow_center,false);
        array.recycle();
        setPadding();
    }

    private void setPadding() {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        int right = getPaddingRight();
        int bottom = getPaddingBottom();
        switch (mArrowDirection){
            case LEFT:
                left +=mArrowWidth;
                break;
            case TOP:
                top += mArrowHeight;
                break;
            case RIGHT:
                right += mArrowWidth;
                break;
            case BOTTOM:
                bottom +=mArrowHeight;
                break;
        }
        setPadding(left,top,right,bottom);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w >0 || h > 0)
            setUp(w,h);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        setUp(getWidth(),getHeight());
    }

    @Override
    public void onDraw(Canvas c) {
        if (null != mBubbleDrawable){
            mBubbleDrawable.draw(c);
        }
        super.onDraw(c);
    }


    private void setUp(int width,int height){
        setUp(0,0,width,height);
    }

    public void setUp(int left,int top,int right,int bottom){
        mBubbleDrawable = new JooyerBubbleDrawable.Builder()
                .rect(new RectF(left,top,right,bottom))
                .arrowWidth(mArrowWidth)
                .arrowHeight(mArrowHeight)
                .arrowOffset(mArrowOffset)
                .arrowCenter(mArrowCenter)
                .arrowDirection(mArrowDirection)
                .radius(mRadius)
                .bubbleColor(mBubbleColor)
                .build();
    }

    public void setArrowOffset(float offset){
        mArrowOffset = offset;
    }
}複製程式碼



沒有什麼註釋,起始和上一篇文章自定義一樣的,所以我們主要就是熟悉這種方式而已!
我們還是瞅一眼 JooyerBubbleDrawable

package com.jooyerbubblemenu;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.Log;

/**
 * Created by Jooyer on 2017/3/11
 */

public class JooyerBubbleDrawable extends Drawable {
    private static final String TAG = JooyerBubbleDrawable.class.getSimpleName();

    /**
     * 儲存座標(自定義控制元件的大小)
     */
    private RectF mRect;

    /**
     * 氣泡的路徑
     */
    private Path mPath = new Path();


    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    /**
     * 箭頭寬度
     */
    private float mArrowWidth;

    /**
     * 箭頭寬度
     */
    private float mArrowHeight;

    /**
     * 圓弧半徑
     */
    private float mRadius;
    /**
     * 箭頭所在位置偏移量
     */
    private float mArrowOffset;

    /**
     * 氣泡背景色
     */
    private int mBubbleColor;


    /**
     * 三角箭頭所在位置
     */
    private ArrowDirection mArrowDirection;


    /**
     * 箭頭是否居中
     */
    private boolean mArrowCenter;

    /**
     *  重寫此方法,在這裡實現和 自定義控制元件中 onDraw 類似的功能
     */
    @Override
    public void draw(Canvas canvas) {
        mPaint.setColor(mBubbleColor);
        setUpPath(mArrowDirection, mPath);
        canvas.drawPath(mPath, mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT; //視窗透明化
    }


    private void setUpPath(ArrowDirection arrowDirection, Path path) {
        switch (arrowDirection) {
            case LEFT:
                setUpLeftPath(mRect, path);
                break;
            case TOP:
                setUpTopPath(mRect, path);
                break;
            case RIGHT:
                setUpRightPath(mRect, path);
                break;
            case BOTTOM:
                setUpBottomPath(mRect,path);
                break;
        }
    }

    /**
     * 箭頭朝左
     */
    private void setUpLeftPath(RectF rect, Path path) {
        if (mArrowCenter)
            mArrowOffset = (rect.bottom - rect.top - mArrowWidth) / 2;

        path.moveTo(rect.left + mArrowWidth + mRadius, rect.top);
        path.lineTo(rect.width() - mRadius, rect.top); // 這裡的rect.width() 是可以使用rect.right
        Log.i(TAG, "====setUpLeftPath========" + (rect.width() - mRadius) + "======= : " + (rect.right - mRadius));

        path.arcTo(new RectF(rect.right - mRadius, rect.top, rect.right, rect.top + mRadius), 270, 90);
        path.lineTo(rect.right, rect.bottom - mRadius);

        path.arcTo(new RectF(rect.right - mRadius, mRect.bottom - mRadius, rect.right, rect.bottom), 0, 90);
        path.lineTo(rect.left + mArrowWidth + mRadius, rect.bottom);

        path.arcTo(new RectF(rect.left + mArrowWidth, rect.bottom - mRadius, rect.left + mArrowWidth + mRadius, rect.bottom), 90, 90);
        path.lineTo(rect.left + mArrowWidth, mArrowHeight + mArrowOffset);
        path.lineTo(rect.left, mArrowOffset + mArrowHeight / 2);
        path.lineTo(rect.left + mArrowWidth, mArrowOffset);
        path.lineTo(rect.left + mArrowWidth, rect.top + mRadius);

        path.arcTo(new RectF(rect.left + mArrowWidth, mRect.top, rect.left + mArrowWidth + mRadius, rect.top + mRadius), 180, 90);
        path.close();

    }

    /**
     * 箭頭朝上
     */
    private void setUpTopPath(RectF rect, Path path) {
        if (mArrowCenter)
            mArrowOffset = (rect.right - rect.left - mArrowWidth) / 2;

        path.moveTo(rect.left + Math.min(mRadius, mArrowOffset), rect.top + mArrowHeight);
        path.lineTo(rect.left + mArrowOffset, rect.top + mArrowHeight);
        path.lineTo(rect.left + mArrowOffset + mArrowWidth / 2, rect.top);
        path.lineTo(rect.left + mArrowOffset + mArrowWidth, rect.top + mArrowHeight);
        path.lineTo(rect.right - mRadius, rect.top + mArrowHeight);

        path.arcTo(new RectF(rect.right - mRadius, rect.top + mArrowHeight, rect.right, rect.top + mArrowHeight + mRadius), 270, 90);
        path.lineTo(rect.right, rect.bottom - mRadius);

        path.arcTo(new RectF(rect.right - mRadius, rect.bottom - mRadius, rect.right, rect.bottom), 0, 90);
        path.lineTo(rect.left + mRadius, rect.bottom);

        path.arcTo(new RectF(rect.left, rect.bottom - mRadius, rect.left + mRadius, rect.bottom), 90, 90);
        path.lineTo(rect.left, rect.top + mArrowHeight + mRadius);

        path.arcTo(new RectF(rect.left, rect.top + mArrowHeight, rect.left + mRadius, rect.top + mArrowHeight + mRadius), 180, 90);
        path.close();
    }

    /**
     * 箭頭朝右
     */
    private void setUpRightPath(RectF rect, Path path) {
        if (mArrowCenter)
            mArrowOffset = (rect.bottom - rect.top - mArrowWidth) / 2;

        path.moveTo(rect.left + mRadius, rect.top);
        path.lineTo(rect.right - mRadius - mArrowWidth, rect.top);

        path.arcTo(new RectF(rect.right - mArrowWidth - mRadius, rect.top, rect.right - mArrowWidth, rect.top + mRadius), 270, 90);
        path.lineTo(rect.right - mArrowWidth, rect.top + mArrowOffset);
        path.lineTo(rect.right, rect.top + mArrowOffset + mArrowHeight / 2);
        path.lineTo(rect.right - mArrowWidth, rect.top + mArrowOffset + mArrowHeight);
        path.lineTo(rect.right - mArrowWidth, rect.bottom - mRadius);

        path.arcTo(new RectF(rect.right - mArrowWidth - mRadius, rect.bottom - mRadius, rect.right - mArrowWidth, rect.bottom), 0, 90);
        path.lineTo(rect.right - mArrowWidth - mRadius, rect.bottom);

        path.arcTo(new RectF(rect.left, rect.bottom - mRadius, rect.left + mRadius, rect.bottom), 90, 90);
        path.lineTo(rect.left, rect.top + mRadius);

        path.arcTo(new RectF(rect.left, rect.top, rect.left + mRadius, rect.top + mRadius), 180, 90);
        path.close();
    }

    /**
     * 箭頭朝下
     */
    private void setUpBottomPath(RectF rect, Path path) {
        if (mArrowCenter)
            mArrowOffset = (rect.right - rect.left - mArrowWidth) / 2;

        path.moveTo(rect.left + mRadius, rect.top);
        path.lineTo(rect.right - mRadius, rect.top);

        path.arcTo(new RectF(rect.right - mRadius, rect.top, rect.right, rect.top + mRadius), 270, 90);
        path.lineTo(rect.right, rect.bottom - mArrowHeight - mRadius);

        path.arcTo(new RectF(rect.right - mRadius, rect.bottom - mArrowHeight - mRadius, rect.right, rect.bottom - mArrowHeight), 0, 90);
        path.lineTo(rect.left + mArrowOffset + mArrowWidth, rect.bottom - mArrowHeight);
        path.lineTo(rect.left + mArrowOffset + mArrowWidth / 2, rect.bottom);
        path.lineTo(rect.left + mArrowOffset, rect.bottom - mArrowHeight);
        path.lineTo(rect.left + mRadius, rect.bottom - mArrowHeight);

        path.arcTo(new RectF(rect.left, rect.bottom - mArrowHeight - mRadius, rect.left + mRadius, rect.bottom - mArrowHeight), 90, 90);
        path.lineTo(rect.left, rect.top + mRadius);

        path.arcTo(new RectF(rect.left, rect.top,rect.left + mRadius,rect.top + mRadius),180,90);
        path.close();
    }

    private JooyerBubbleDrawable(Builder builder) {
        this.mRect = builder.mRectF;
        this.mRadius = builder.mRadius;
        this.mArrowWidth = builder.mArrowWidth;
        this.mArrowHeight = builder.mArrowHeight;
        this.mArrowOffset = builder.mArrowOffset;
        this.mBubbleColor = builder.mBubbleColor;
        this.mArrowDirection = builder.mArrowDirection;
        this.mArrowCenter = builder.mArrowCenter;
    }


    /**
     * 建造者模式
     */
    public static class Builder {
        /**
         * 箭頭預設寬度
         */
        public static float DEFAULT_ARROW_WIDTH = 25;
        /**
         * 箭頭預設高度
         */
        public static float DEFAULT_ARROW_HEIGHT = 25;
        /**
         * 預設圓角半徑
         */
        public static float DEFAULT_RADIUS = 20;
        /**
         * 預設箭頭偏移量
         */
        public static float DEFAULT_ARROW_OFFSET = 50;
        /**
         * 氣泡預設背景顏色
         */
        public static int DEFAULT_BUBBLE_COLOR = Color.RED;

        private RectF mRectF;
        private float mArrowWidth = DEFAULT_ARROW_WIDTH;
        private float mArrowHeight = DEFAULT_ARROW_HEIGHT;
        private float mRadius = DEFAULT_RADIUS;
        private float mArrowOffset = DEFAULT_ARROW_OFFSET;

        private int mBubbleColor = DEFAULT_BUBBLE_COLOR;
        private ArrowDirection mArrowDirection = ArrowDirection.LEFT;
        private boolean mArrowCenter;

        public Builder rect(RectF rect) {
            this.mRectF = rect;
            return this;
        }

        public Builder arrowWidth(float width) {
            this.mArrowWidth = width;
            return this;
        }

        public Builder arrowHeight(float height) {
            this.mArrowHeight = height;
            return this;
        }

        public Builder radius(float angle) {
            this.mRadius = angle; //TODO
            return this;
        }

        public Builder arrowOffset(float position) {
            this.mArrowOffset = position;
            return this;
        }

        public Builder bubbleColor(int color) {
            this.mBubbleColor = color;
            return this;
        }


        public Builder arrowDirection(ArrowDirection direction) {
            this.mArrowDirection = direction;
            return this;
        }

        public Builder arrowCenter(boolean arrowCenter) {
            this.mArrowCenter = arrowCenter;
            return this;
        }

        public JooyerBubbleDrawable build() {
            if (null == mRectF) {
                throw new IllegalArgumentException("BubbleDrawable RectF can not be null");
            }
            return new JooyerBubbleDrawable(this);
        }
    }



    /**
     * 箭頭位置
     */
    public enum ArrowDirection {
        LEFT(0x00),
        TOP(0x01),
        RIGHT(0x02),
        BOTTOM(0x03);

        private int mValue;

        ArrowDirection(int value) {
            mValue = value;
        }

        private int getIntValue() {
            return mValue;
        }

        public static ArrowDirection getDefault() {
            return LEFT;
        }

        public static ArrowDirection mapIntToValue(int stateInt) {
            for (ArrowDirection value : ArrowDirection.values()) {
                if (stateInt == value.getIntValue()) {
                    return value;
                }
            }
            return getDefault();
        }
    }


}複製程式碼



自定義屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="JooyerBubbleRecyclerView">
        <!-- 三角箭頭寬度 -->
        <attr name="jooyer_bubble_arrow_width" format="dimension"/>
        <!-- 三角箭頭高度 -->
        <attr name="jooyer_bubble_arrow_height" format="dimension"/>
        <!-- 三角箭頭位置(相對偏移量) -->
        <attr name="jooyer_bubble_arrow_offset" format="dimension"/>
        <!-- 氣泡圓角半徑 -->
        <attr name="jooyer_bubble_arrow_radius" format="dimension"/>
        <!-- 氣泡背景顏色 -->
        <attr name="jooyer_bubble_arrow_color" format="color"/>
        <!-- 三角箭頭是否居中 -->
        <attr name="jooyer_bubble_arrow_center" format="boolean"/>
        <!-- 三角箭頭方向朝向 -->
        <attr name="jooyer_bubble_arrow_direction" format="enum">
            <enum name="jooyer_bubble_arrow_direction_left" value="0x00"/>
            <enum name="jooyer_bubble_arrow_direction_top" value="0x01"/>
            <enum name="jooyer_bubble_arrow_direction_right" value="0x02"/>
            <enum name="jooyer_bubble_arrow_direction_bottom" value="0x03"/>
        </attr>
    </declare-styleable>

</resources>複製程式碼

我們看下它的具體用法:

<?xml version="1.0" encoding="utf-8"?>
<com.jooyerbubblemenu.JooyerBubbleRecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:toools="http://schemas.android.com/tools"
    android:id="@+id/rv_top_right_menu"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:jooyer_bubble_arrow_width="8dp"
    app:jooyer_bubble_arrow_height="10dp"
    app:jooyer_bubble_arrow_offset="20dp"
    app:jooyer_bubble_arrow_radius="10dp"
    app:jooyer_bubble_arrow_center="false"
    app:jooyer_bubble_arrow_color="#fff"
    app:jooyer_bubble_arrow_direction="jooyer_bubble_arrow_direction_top"
    toools:itemlist="@layout/top_right_menu_item"
    />複製程式碼

接下來,我們看看大神封裝的Item和PopupWindow

package com.jooyerbubblemenu;

/**
 * 每一項選單
 * Created by Jooyer on 2017/2/10
 */
public class MenuItem {

    private int icon;
    private String content;
    public MenuItem() {
    }

    public MenuItem(int icon, String content) {
        this.icon = icon;
        this.content = content;
    }


    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}複製程式碼

大神的神劍: TopRightMenu

package com.jooyerbubblemenu;

import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow;

import com.jooyerbubblemenu.utils.LogUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 封裝一個PopupWindow 實現類似QQ,支付寶等右上角彈框效果
 * Created by Jooyer on 2017/2/10
 */
public class TopRightMenu {
    private static final String TAG = "TopRightMenu";
    private static final int DEFAULT_AMEND = 200;
    private Context mContext;

    private PopupWindow mPopupWindow;
    private RecyclerView mRecyclerView;
    private View mParent;

    private TopRightMenuAdapter mTopRightMenuAdapter;
    private List<MenuItem> mItemList;

    /**
     * 彈窗預設高度
     */
    private static final int DEFAULT_HEIGHT = 480;
    /**
     * 彈窗預設高度
     */
    private static final int DEFAULT_WIDTH = 320;
    private int mPopupHeight = DEFAULT_HEIGHT;
    private int mPopupWidth = DEFAULT_WIDTH;

    /**
     * 預設顯示圖示
     */
    private boolean isShowIcon = true;

    /**
     * 預設顯示背景 --> 背景變暗
     */
    private boolean isShowBackground = true;

    /**
     * 預設顯示動畫
     */
    private boolean isShowAnimationStyle = true;

    /**
     * 預設彈出或者關閉動畫
     */
    private static final int DEFAULT_ANIM_STYLE = R.style.TopRightMenu_Anim;
    private int mAnimationStyle;

    /**
     * 預設的透明度值
     */
    private float mAlpha = 0.7f;


    public TopRightMenu(Context context) {
        mContext = context;
        init();
    }

    private void init() {
        mParent = LayoutInflater.from(mContext).inflate(R.layout.top_right_menu, null);
        mRecyclerView = (RecyclerView) mParent.findViewById(R.id.rv_top_right_menu);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
        mItemList = new ArrayList<>();
        mTopRightMenuAdapter = new TopRightMenuAdapter(mContext, mItemList, isShowIcon, this);
    }

    /**
     * 設定高度
     */
    public TopRightMenu setHeight(int height) {
        if (height > 0) {
            this.mPopupHeight = height;
        }
        return this;
    }

    /**
     * 設定寬度
     */
    public TopRightMenu setWidth(int width) {
        if (width > 0) {
            this.mPopupWidth = width;
        } else {
            throw new IllegalArgumentException("寬度不能為空,且必須大於0!");
        }
        return this;
    }

    /**
     * 設定是否顯示圖示
     */
    public TopRightMenu setShowIcon(boolean isShowIcon) {
        this.isShowIcon = isShowIcon;
        return this;
    }

    /**
     * 設定背景是否變暗
     */
    public TopRightMenu setShowBackground(boolean isShowBackground) {
        this.isShowBackground = isShowBackground;
        return this;
    }

    /**
     * 設定背景顏色變化動畫
     *
     * @param from     --> 開始值
     * @param to       --> 結束值
     * @param duration --> 持續時間
     */
    private void setBackgroundAlpha(float from, float to, int duration) {
        final WindowManager.LayoutParams lp = ((Activity) mContext).getWindow().getAttributes();
        ValueAnimator animator = ValueAnimator.ofFloat(from, to);
        animator.setDuration(duration);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                lp.alpha = (float) animation.getAnimatedValue();
                ((Activity) mContext).getWindow().setAttributes(lp);
            }
        });
        animator.start();
    }


    /**
     * 設定是否顯示動畫
     */
    public TopRightMenu setShowAnimationStyle(boolean isShowAnimationStyle) {
        this.isShowAnimationStyle = isShowAnimationStyle;
        return this;
    }

    /**
     * 設定動畫
     */
    public TopRightMenu setAnimationStyle(int animationStyle) {
        this.mAnimationStyle = animationStyle;
        return this;
    }


    /**
     * 新增單個選單
     */
    public TopRightMenu addMenuItem(MenuItem menuItem) {
        mItemList.add(menuItem);
        return this;
    }

    /**
     * 新增多個選單
     */
    public TopRightMenu addMenuItems(List<MenuItem> list) {
        mItemList.addAll(list);
        return this;
    }

    public TopRightMenu setOnTopRightMenuItemClickListener(OnTopRightMenuItemClickListener listener) {
        mTopRightMenuAdapter.setOnTopRightMenuItemClickListener(listener);
        return this;
    }

    public TopRightMenu showAsDropDown(View anchor) {
        showAsDropDown(anchor, 0, 0);
        return this;
    }
    public TopRightMenu setArrowPosition(float value) {
        if (mRecyclerView != null && mRecyclerView instanceof JooyerBubbleRecyclerView) {
            ((JooyerBubbleRecyclerView) mRecyclerView).setArrowOffset(value);
        }
        return this;
    }

    public TopRightMenu showAsDropDown(View anchor, int offsetX, int offsetY) {
        if (null == mPopupWindow) {
            mPopupWindow = getPopupWindow();
        }

        if (!mPopupWindow.isShowing()) {
            mPopupWindow.showAsDropDown(anchor, offsetX, offsetY);
            if (isShowBackground)
                setBackgroundAlpha(1f, mAlpha, 300);
        }

        return this;
    }


    public TopRightMenu show(View anchor, Rect frame, Point origin) {

        if (null == mPopupWindow) {
            mPopupWindow = getPopupWindow();
        }

        if (null == frame) frame = new Rect();
        if (null == origin) origin = new Point(-1, -1);

        int[] location = reviseFrameAndOrigin(anchor, frame, origin);
        int x = location[0];
        int y = location[1];

        LogUtils.i(TAG, "==location====X : " + x + "======Y : " + y);

        int width = anchor.getWidth();
        int height = anchor.getHeight();

        int contentHeight = mPopupWindow.getContentView().getMeasuredHeight();
        if (!mPopupWindow.isShowing()) {

            if (y + height + contentHeight < frame.bottom) {
                mPopupWindow.showAsDropDown(anchor, (int) (-DEFAULT_AMEND - (mPopupWidth - DEFAULT_WIDTH)), 0);
                LogUtils.i(TAG, "=====showAsDropDown=====" + (int) (-DEFAULT_AMEND - (mPopupWidth - DEFAULT_WIDTH)));
            }
            if (isShowBackground)
                setBackgroundAlpha(1f, mAlpha, 300);
        }
        return this;
    }


    /**
     * 確定 彈框的位置
     */
    private int[] reviseFrameAndOrigin(View anchor, Rect frame, Point origin) {
        int[] location = new int[2];
        anchor.getLocationInWindow(location);
        if (origin.x < 0 || origin.y < 0) {
            origin.set(anchor.getWidth() >> 1, anchor.getHeight() >> 1);
            LogUtils.i(TAG, "====1====" + (anchor.getWidth() >> 1) + "=====" + (anchor.getHeight() >> 1)
                    + "=======getWidth : " + anchor.getWidth() + "===== : " + anchor.getHeight());
        }
        if (frame.isEmpty() || !frame.contains(origin.x + location[0], origin.y + location[1])) {
            anchor.getWindowVisibleDisplayFrame(frame);
        }
        LogUtils.i(TAG, "====2====" + (origin.x + location[0]) + "=====" + (origin.y + location[1]));
        return location;
    }


    private PopupWindow getPopupWindow() {
        mPopupWindow = new PopupWindow(mContext);
        mPopupWindow.setContentView(mParent);
        mPopupWindow.setWidth(mPopupWidth);
        mPopupWindow.setHeight(mPopupHeight);
        if (isShowAnimationStyle)
            mPopupWindow.setAnimationStyle(mAnimationStyle <= 0 ? DEFAULT_ANIM_STYLE : mAnimationStyle);

        mPopupWindow.setFocusable(true);
        mPopupWindow.setOutsideTouchable(true);
        mPopupWindow.setBackgroundDrawable(new ColorDrawable());
        mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                if (isShowBackground)
                    setBackgroundAlpha(mAlpha, 1f, 300);
            }
        });

        mTopRightMenuAdapter.setItemData(mItemList);
        mTopRightMenuAdapter.setShowIcon(isShowIcon);
        mRecyclerView.setAdapter(mTopRightMenuAdapter);
        return mPopupWindow;
    }


    public void dismiss() {
        if (null != mPopupWindow && mPopupWindow.isShowing())
            mPopupWindow.dismiss();
    }


}複製程式碼



以上我在不明白的地方列印有日誌輸入,可以瞭解大神用意
最後來看哈使用方式:

package com.jooyerbubblemenu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private ImageView more;
    private TopRightMenu mTopRightMenu;
    private List<MenuItem> mItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initMenu();
        more = (ImageView) findViewById(R.id.more);
        mTopRightMenu = new TopRightMenu(this);
        more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              mTopRightMenu.setWidth(400)
                      .setHeight(600)
                      .setShowIcon(true)
                      .setShowAnimationStyle(true)
                      .setAnimationStyle(R.style.TopRightMenu_Anim)
                      .setShowBackground(true)
                      .setArrowPosition(345f)
                      .addMenuItems(mItems)
                      .addMenuItem(new MenuItem(R.mipmap.facetoface,"面對面快傳"))
                      .addMenuItem(new MenuItem(R.mipmap.pay,"付款"))
                      .setOnTopRightMenuItemClickListener(new OnTopRightMenuItemClickListener() {
                          @Override
                          public void onTopRightMenuItemClick(int position) {
                              Toast.makeText(MainActivity.this, " 點選位置 :" + position, Toast.LENGTH_SHORT).show();
                          }
                      });

                mTopRightMenu.show(v,null,null);
            }
        });
    }

    private void initMenu() {
        mItems = new ArrayList<>();
        mItems.add(new MenuItem(R.mipmap.multichat, "發起多人聊天"));
        mItems.add(new MenuItem(R.mipmap.addmember, "加好友"));
        mItems.add(new MenuItem(R.mipmap.qr_scan, "掃一掃"));
    }
}複製程式碼

其執行結果,如開頭所示,這裡主要是對上次 Path 的學習的鞏固!下一次,我將繼續站在大神肩膀上大家分享
附上本次 github 地址: github.com/Jooyer/Jooy…

相關文章