回爐再造媚狰,靈活的YMenuView2.0誕生

出處
炎之鎧郵箱:yanzhikai_yjk@qq.com
博客地址:http://blog.csdn.net/totond
本文原創(chuàng)型檀,轉(zhuǎn)載請(qǐng)注明本出處憎妙!
本項(xiàng)目GitHub地址:https://github.com/totond/YMenuView
歡迎 Star or Fork甚纲!

前言

之前把我項(xiàng)目用到的類似于PathView的菜單YMenuView抽離出來(lái)口锭,分享了實(shí)現(xiàn)思路,但是只是實(shí)現(xiàn)了在右下角的幾種菜單彈出收回的效果,限制太大鹃操,適用性不強(qiáng)韭寸。這次重溫了一下設(shè)計(jì)模式之后把YMenuView回爐再造,重構(gòu)代碼荆隘,打造出一個(gè)可以讓用戶發(fā)揮自己自由想象恩伺,實(shí)現(xiàn)自己想法需求的靈活YMenuView2.0,還加了3個(gè)自定義YMenu的例子椰拒,先來(lái)看看看效果:


(前面幾個(gè)是之前YMenuView1.x的效果晶渠,后面是新加的)

介紹

YMenuView是包含著一個(gè)MenuButton和若干個(gè)OptionButton的RelativeLayout,具體實(shí)現(xiàn)原理前一篇已經(jīng)有介紹了燃观,所以這篇文章主要說(shuō)明YMenuView2.0的改變(使用方法的話可以直接看Github褒脯。YMenuView2.0重構(gòu)代碼之后,最大的改變就是把YMenuView的大部分邏輯抽離出來(lái)缆毁,留下4個(gè)主要的抽象方法番川,可以讓用戶自定義,這4個(gè)分別是決定:MenuButton的位置脊框、OptionButton的位置颁督、OptionButton的顯示動(dòng)畫(huà)和OptionButton的消失動(dòng)畫(huà)。

重構(gòu)過(guò)程

抽象類YMenu

YMenuView2.0的主角不再是YMenuView類浇雹,現(xiàn)在的它只是一個(gè)小弟(子類)沉御,真正的大哥現(xiàn)在叫YMenu(父類)
  最近正在學(xué)習(xí)Animation的源碼,得知Animation是一個(gè)抽象類箫爷,然后其他具體的動(dòng)畫(huà)時(shí)通過(guò)繼承它嚷节,然后重寫(xiě)applyTransformation()方法來(lái)實(shí)現(xiàn)具體的動(dòng)畫(huà)需求,這是模板方法模式的應(yīng)用(其實(shí)Activity也這樣)虎锚,所以YMenuView也參考這種模式硫痰,把一些次要的邏輯抽象出來(lái),放都在抽象父類YMenu里面窜护,開(kāi)放出4個(gè)抽象方法給用戶可以通過(guò)繼承來(lái)自定義YMenu:

    /**
     * 設(shè)置MenuButton的位置,重寫(xiě)該方法進(jìn)行自定義設(shè)置
     *
     * @param menuButton 傳入傳入MenuButton效斑,此時(shí)它的寬高位置屬性還未設(shè)置,需要在此方法設(shè)置柱徙。
     */
    public abstract void setMenuPosition(View menuButton);

    /**
     * 設(shè)置OptionButton的位置,重寫(xiě)該方法進(jìn)行自定義設(shè)置
     *
     * @param optionButton 傳入OptionButton缓屠,此時(shí)它的寬高位置屬性還未設(shè)置,需要在此方法設(shè)置护侮。
     * @param menuButton   傳入MenuButton敌完,此時(shí)它已經(jīng)初始化完畢,可以利用羊初。
     * @param index        傳入的是該OptionButton的索引滨溉,用于區(qū)分不同OptionButton什湘。
     */
    public abstract void setOptionPosition(OptionButton optionButton, View menuButton, int index);

    /**
     * 設(shè)置OptionButton的顯示動(dòng)畫(huà),重寫(xiě)該方法進(jìn)行自定義設(shè)置
     *
     * @param optionButton 傳入了該動(dòng)畫(huà)所屬的OptionButton,此時(shí)它的寬高位置屬性已初始化完畢晦攒,可以利用闽撤。
     * @param index        傳入的是該OptionButton的索引,用于區(qū)分不同OptionButton脯颜。
     * @return             返回的是創(chuàng)建好的動(dòng)畫(huà)                    
     */
    public abstract Animation createOptionShowAnimation(OptionButton optionButton, int index);


    /**
     * 設(shè)置OptionButton的消失動(dòng)畫(huà),重寫(xiě)該方法進(jìn)行自定義設(shè)置
     *
     * @param optionButton 傳入了該動(dòng)畫(huà)所屬的OptionButton哟旗,此時(shí)它的寬高位置屬性已初始化完畢,可以利用栋操。
     * @param index        傳入的是該OptionButton的索引闸餐,用于區(qū)分不同OptionButton。
     * @return             返回的是創(chuàng)建好的動(dòng)畫(huà)
     */
    public abstract Animation createOptionDisappearAnimation(OptionButton optionButton, int index);

YMenu有4個(gè)具體實(shí)現(xiàn)類(效果都在上面的gif圖里展現(xiàn)過(guò)了):

抽象方法的實(shí)現(xiàn)

這4個(gè)方法抽象出來(lái)之后矾芙,由子類實(shí)現(xiàn)功能绎巨,下面介紹如何實(shí)現(xiàn)(以下的4個(gè)方法介紹的代碼是在YMenuView里面的):

1.setMenuPosition()方法

此方法的實(shí)現(xiàn)決定了MenuButton的位置,MenuButton就是上面效果圖那個(gè)齒輪蠕啄,點(diǎn)擊之后會(huì)有選項(xiàng)出現(xiàn)消失的動(dòng)作。在上一篇YMenuView源碼分析里面就已經(jīng)寫(xiě)到戈锻,它的位置是用Relative的LayoutParams來(lái)設(shè)置的(和上一篇有點(diǎn)不同就是因?yàn)槭菑某橄蟾割惈@取屬性歼跟,所以要用一些get方法,而且我還重命名了一些屬性格遭,具體可以在Github的使用介紹屬性表查看):

    @Override
    public void setOptionPosition(OptionButton optionButton, View menuButton, int index){

        LayoutParams layoutParams = new LayoutParams(getYMenuButtonWidth(), getYMenuButtonHeight());
        layoutParams.setMarginEnd(getYMenuToParentXMargin());
        layoutParams.bottomMargin = getYMenuToParentYMargin();
        layoutParams.addRule(ALIGN_PARENT_RIGHT);
        layoutParams.addRule(ALIGN_PARENT_BOTTOM);

        menuButton.setLayoutParams(layoutParams);
    }

但是這樣使用LayoutParams的話理解起來(lái)很不直觀(其實(shí)熟悉RelativeLayout的還好)哈街,如果讓用戶自定義的話會(huì)有點(diǎn)麻煩,所以我把這個(gè)過(guò)程參考建造者模式封裝成了一個(gè)類MenuPositionBuilder拒迅,通過(guò)它來(lái)實(shí)現(xiàn)這個(gè)位置的設(shè)置過(guò)程骚秦。

    @Override
    public void setMenuPosition(View menuButton){
        new MenuPositionBuilder(menuButton)
                //設(shè)置寬高
                .setWidthAndHeight(getYMenuButtonWidth(), getYMenuButtonHeight())
                //設(shè)置參考方向
                .setMarginOrientation(PositionBuilder.MARGIN_RIGHT,PositionBuilder.MARGIN_BOTTOM)
                //設(shè)置是否在XY方向處于中心
                .setIsXYCenter(false,false)
                //設(shè)置XY方向的參考,如果設(shè)置了MARGIN_LEFT和MARGIN_TOP璧微,那么XMargin和YMargin就是與參照物左邊界和上邊界的距離
                .setXYMargin(getYMenuToParentXMargin(),getYMenuToParentYMargin())
                //最后確認(rèn)時(shí)候調(diào)用
                .finish();
    }

這樣的話感覺(jué)就清晰一些了作箍,當(dāng)然有人如果熟悉了RelativeLayout參數(shù)的話,可能會(huì)覺(jué)得比原來(lái)復(fù)雜了前硫,也可以用上面的寫(xiě)法胞得,這兩者是等價(jià)的。至于MenuPositionBuilder的實(shí)現(xiàn)過(guò)程屹电,運(yùn)用的建造者模式阶剑,比較簡(jiǎn)單,由于篇幅原因這里就不寫(xiě)了危号,有興趣的話可以直接到項(xiàng)目Github地址上查看源碼牧愁。

2.setOptionPosition()方法

此方法的實(shí)現(xiàn)決定了那些選項(xiàng)OptionButton的位置,這里是YMenuView的實(shí)現(xiàn):

    @Override
    public void setOptionPosition(OptionButton optionButton, View menuButton, int index){
        //設(shè)置動(dòng)畫(huà)模式和時(shí)長(zhǎng)
        optionButton.setSD_Animation(getOptionSD_AnimationMode());
        optionButton.setDuration(getOptionSD_AnimationDuration());

        //計(jì)算OptionButton的位置
        int position = index % getOptionColumns();

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
                getYOptionButtonWidth()
                , getYOptionButtonHeight());

        layoutParams.rightMargin = getYOptionToParentXMargin()
                + getYOptionXMargin() * position
                + getYOptionButtonWidth() * position;

        layoutParams.bottomMargin = getYOptionToParentYMargin()
                + (getYOptionButtonHeight() + getYOptionYMargin())
                * (index / getOptionColumns());
        layoutParams.addRule(ALIGN_PARENT_BOTTOM);
        layoutParams.addRule(ALIGN_PARENT_RIGHT);

        optionButton.setLayoutParams(layoutParams);
    }

和MenuButton一樣外莲,這個(gè)方法也有一個(gè)專屬的OptionPositionBuilder猪半,下面使用它來(lái)設(shè)置:

    @Override
    public void setOptionPosition(OptionButton optionButton, View menuButton, int index){
        //設(shè)置動(dòng)畫(huà)模式和時(shí)長(zhǎng)
        optionButton.setSD_Animation(getOptionSD_AnimationMode());
        optionButton.setDuration(getOptionSD_AnimationDuration());

        //計(jì)算OptionButton的位置
        int position = index % getOptionColumns();

        new OptionPositionBuilder(optionButton,menuButton)
                //設(shè)置寬高
                .setWidthAndHeight(getYOptionButtonWidth(), getYOptionButtonHeight())
                //設(shè)置在XY方向是否以MenuButton作為參照物
                .isAlignMenuButton(false,false)
                //設(shè)置參考方向
                .setMarginOrientation(PositionBuilder.MARGIN_RIGHT,PositionBuilder.MARGIN_BOTTOM)
                //設(shè)置XY方向的距離,如果設(shè)置了MARGIN_LEFT和MARGIN_TOP,那么XMargin和YMargin就是與參照物左邊界和上邊界的距離
                .setXYMargin(
                        getYOptionToParentXMargin()
                                + getYOptionXMargin() * position
                                + getYOptionButtonWidth() * position,
                        getYOptionToParentYMargin()
                                + (getYOptionButtonHeight() + getYOptionYMargin())
                                * (index / getOptionColumns()))
                //最后確認(rèn)時(shí)候調(diào)用
                .finish();
    }

這里YMenuView的OptionButton位置具體的實(shí)現(xiàn)邏輯上一篇介紹YMenuView1.x的時(shí)候已經(jīng)說(shuō)過(guò)了办龄,這里就不詳細(xì)說(shuō)了烘绽。

3.createOptionShowAnimation()方法和createOptionDisappearAnimation()方法

這兩個(gè)方法用來(lái)決定OptionButton的出現(xiàn)和消失的動(dòng)畫(huà),原本在YMenuView1.x版本是在OptionButton內(nèi)部實(shí)現(xiàn)俐填,但是為了我們可以通過(guò)繼承YMenu來(lái)實(shí)現(xiàn)一個(gè)自定義的效果安接,所以就通過(guò)一個(gè)回調(diào)設(shè)計(jì)把這兩個(gè)方法的具體實(shí)現(xiàn)放到Y(jié)Menu里抽象出來(lái)了。
  所以英融,這個(gè)方法就是給出一個(gè)索引為index的OptionButton的參數(shù)盏檐,給設(shè)置動(dòng)畫(huà)作為參考值,然后把設(shè)置好的動(dòng)畫(huà)返回驶悟。

    @Override
    public Animation createOptionShowAnimation(OptionButton optionButton, int index){
        AnimationSet animationSet = new AnimationSet(true);
        AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
        alphaAnimation.setDuration(getOptionSD_AnimationDuration());
        TranslateAnimation translateAnimation = new TranslateAnimation(0,0,0,0);
        switch (optionButton.getmSD_Animation()){
            //從MenuButton的左邊移入
            case FROM_BUTTON_LEFT:
                translateAnimation= new TranslateAnimation(getYMenuButton().getX() - optionButton.getRight(),0
                        ,0,0);
                translateAnimation.setDuration(getOptionSD_AnimationDuration());
                break;
            case FROM_RIGHT:
                //從右邊緣移入
                translateAnimation= new TranslateAnimation((getWidth() - optionButton.getX()),0,0,0);
                translateAnimation.setDuration(getOptionSD_AnimationDuration());
//                showAnimation.setInterpolator(new OvershootInterpolator(1.3f));
                break;
            case FROM_BUTTON_TOP:
                //從MenuButton的上邊緣移入
                translateAnimation= new TranslateAnimation(0,0,
                        getYMenuButton().getY() - optionButton.getBottom(),0);
                translateAnimation.setDuration(getOptionSD_AnimationDuration());
                break;
            case FROM_BOTTOM:
                //從下邊緣移入
                translateAnimation = new TranslateAnimation(0,0,getHeight() - optionButton.getY(),0);
                translateAnimation.setDuration(getOptionSD_AnimationDuration());
        }

        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(alphaAnimation);
        return animationSet;
    }

這里的邏輯其實(shí)和之前的沒(méi)有變化胡野,也是要注意Animation里面的坐標(biāo)原點(diǎn)是View的左上角就行了,這里只列出ShowAnimation的實(shí)現(xiàn)痕鳍,至于DisappearAnimation的話就是和這個(gè)相反而已硫豆,篇幅原因就不列出來(lái)了。

4.抽象方法的執(zhí)行順序

這4個(gè)抽象方法是有執(zhí)行順序的笼呆,所以后面的方法能用到前面設(shè)置好的對(duì)象參數(shù):



  這些順序的具體細(xì)節(jié)就是:

  • 先初始化MenuButton,讓MenuButton經(jīng)歷Measure和Layout過(guò)程之后熊响,再初始化OptionButton,這樣OptionButton就能以MenuButton的位置信息做為參考诗赌。
  • 跟著每一個(gè)OptionButton經(jīng)歷Measure和Layout過(guò)程之后汗茄,它的ShowAnimation和DisappearAnimation又能以它的寬高信息進(jìn)行初始化。
  • 這樣根據(jù)不同時(shí)期放出了不同抽象方法铭若,讓用戶可以通過(guò)實(shí)現(xiàn)這4個(gè)方法來(lái)實(shí)現(xiàn)自定義的YMenu洪碳。

Circle8YMenu的實(shí)現(xiàn)


  這是一個(gè)OptionButton圍繞著MenuButton的布局,Option最大數(shù)量為8個(gè)叼屠,MenuButton的位置位于ViewGroup正中間瞳腌,如果想改變的話可以繼承Circle8YMenu,單單重寫(xiě)setMenuPosition()方法就可以了环鲤。

    //8個(gè)Option位置的x纯趋、y乘積因子
    private float[] xyTimes = {0,0.707f,1,0.707f,0,-0.707f,-1,-0.707f};

    //設(shè)置MenuButton的位置,這里設(shè)置成位于ViewGroup中心
    @Override
    public void setMenuPosition(View menuButton) {
        new MenuPositionBuilder(menuButton)
                .setWidthAndHeight(getYMenuButtonWidth(),getYMenuButtonHeight())
                .setMarginOrientation(PositionBuilder.MARGIN_RIGHT,PositionBuilder.MARGIN_BOTTOM)
                .setIsXYCenter(true,true)
                .setXYMargin(getYMenuToParentXMargin(),getYMenuToParentYMargin())
                .finish();
    }

    //設(shè)置OptionButton的位置冷离,這里是設(shè)置成圓形圍繞著MenuButton
    @Override
    public void setOptionPosition(OptionButton optionButton, View menuButton, int index) {
        if (index >= 8){
            try {
                throw new Exception("Circle8YMenuView的OptionPosition最大數(shù)量為8吵冒,超過(guò)將會(huì)發(fā)生錯(cuò)誤");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        int centerX = menuButton.getLeft() + menuButton.getWidth()/2;
        int centerY = menuButton.getTop() + menuButton.getHeight()/2;
        int halfOptionWidth = getYOptionButtonWidth()/2;
        int halfOptionHeight = getYOptionButtonHeight()/2;
        //利用乘積因子來(lái)決定不同位置
        float x = xyTimes[index % 8];
        float y = xyTimes[(index + 6) % 8];

        OptionPositionBuilder OptionPositionBuilder = new OptionPositionBuilder(optionButton,menuButton);
        OptionPositionBuilder
                .isAlignMenuButton(false,false)
                .setWidthAndHeight(getYOptionButtonWidth(), getYOptionButtonHeight())
                .setMarginOrientation(PositionBuilder.MARGIN_LEFT,PositionBuilder.MARGIN_TOP)
                //計(jì)算OptionButton的位置
                .setXYMargin(
                        (int)(centerX + x * getYOptionXMargin() - halfOptionWidth)
                        ,(int)(centerY + y * getYOptionXMargin() - halfOptionHeight)
                )
                .finish();
    }

這里設(shè)置了MenuButton和OptionButton的位置。MenuButton的位置沒(méi)什么好說(shuō)的西剥,直接居中痹栖,而關(guān)于OptionButton的位置這里用到了乘積因子,我覺(jué)得這是一種比較方便的算法瞭空,在這里解釋一下:把MenuButton的中心看作參考點(diǎn)揪阿,以optionXMargin為半徑疗我,OptionButton中心到參考點(diǎn)(這些在XML定義的屬性到了這里怎么用就看自己了,這里就用optionXMargin)南捂,然后x吴裤,y乘以optionXMargin就是OptionButton到參考點(diǎn)的x,y方向距離。

以這個(gè)0號(hào)OptionButton為例溺健,它的x=0麦牺,y=-1,然后是以ViewGroup的左和上邊緣為參考鞭缭,所以它就處于MenuButton的正上方相隔optionXMargin的位置剖膳。

然后就是動(dòng)畫(huà)的實(shí)現(xiàn):

    //設(shè)置OptionButton的顯示動(dòng)畫(huà)
    @Override
    public Animation createOptionShowAnimation(OptionButton optionButton, int index) {
        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation translateAnimation= new TranslateAnimation(
                getYMenuButton().getX() - optionButton.getX()
                ,0
                ,getYMenuButton().getY() - optionButton.getY()
                ,0);
        translateAnimation.setDuration(getOptionSD_AnimationDuration());
        AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
        alphaAnimation.setDuration(getOptionSD_AnimationDuration());
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(translateAnimation);
        //為不同的Option設(shè)置延時(shí)
        if (index % 2 == 1) {
            animationSet.setStartOffset(getOptionSD_AnimationDuration()/2);
        }
        return animationSet;
    }

    //設(shè)置OptionButton的消失動(dòng)畫(huà)
    @Override
    public Animation createOptionDisappearAnimation(OptionButton optionButton, int index) {
        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation translateAnimation= new TranslateAnimation(
                0
                ,getYMenuButton().getX() - optionButton.getX()
                ,0
                ,getYMenuButton().getY() - optionButton.getY()
        );
        translateAnimation.setDuration(getOptionSD_AnimationDuration());
        AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
        alphaAnimation.setDuration(getOptionSD_AnimationDuration());
        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(alphaAnimation);
        //為不同的Option設(shè)置延時(shí)
        if (index % 2 == 0) {
            animationSet.setStartOffset(getOptionSD_AnimationDuration()/2);
        }
        return animationSet;
    }

動(dòng)畫(huà)都是選項(xiàng)從MenuButton里面冒出來(lái)和滾回去,亮點(diǎn)就是根據(jù)OptionButton的index設(shè)置了不同的延時(shí)岭辣,讓整個(gè)效果看起來(lái)炫酷一點(diǎn)吱晒。

重寫(xiě)了這4個(gè)方法之后,就能夠創(chuàng)造出這個(gè)Circle8YMenu了沦童,如果想在這基礎(chǔ)上改東西仑濒,直接重寫(xiě)這4個(gè)方法中要改變的方法就行了,有點(diǎn)方便偷遗。

TreeYMenu的實(shí)現(xiàn)


  這是一個(gè)OptionButton分布成分叉樹(shù)的布局躏精,Option最大數(shù)量為9個(gè),MenuButton的位置位于ViewGroup中下方鹦肿。

    //9個(gè)Option位置的x、y乘積因子
    private static final float[] xTimes = {-1,1,0,-2,-2,2,2,-1,1};
    private static final float[] yTimes = {-1,-1,-2,0,-2,0,-2,-3,-3};

    //設(shè)置MenuButton的位置辅柴,這是設(shè)置在屏幕中下方
    @Override
    public void setMenuPosition(View menuButton) {
        new MenuPositionBuilder(menuButton)
                //設(shè)置寬高
                .setWidthAndHeight(getYMenuButtonWidth(), getYMenuButtonHeight())
                //設(shè)置參考方向
                .setMarginOrientation(PositionBuilder.MARGIN_RIGHT,PositionBuilder.MARGIN_BOTTOM)
                //設(shè)置是否在XY方向處于中心
                .setIsXYCenter(true,false)
                //設(shè)置XY方向距離
                .setXYMargin(getYMenuToParentXMargin(),getYMenuToParentYMargin())
                .finish();
    }

    //設(shè)置OptionButton的位置箩溃,這里是把9個(gè)Option設(shè)置為樹(shù)狀布局
    @Override
    public void setOptionPosition(OptionButton optionButton, View menuButton, int index) {
        if (index > 8){
            try {
                throw new Exception("TreeYMenuView的OptionPosition最大數(shù)量為9,超過(guò)將會(huì)發(fā)生錯(cuò)誤");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        int centerX = menuButton.getLeft() + menuButton.getWidth()/2;
        int centerY = menuButton.getTop() + menuButton.getHeight()/2;
        int halfOptionWidth = getYOptionButtonWidth()/2;
        int halfOptionHeight = getYOptionButtonHeight()/2;

        //利用乘積因子來(lái)決定不同位置
        float x = xTimes[index];
        float y = yTimes[index];
        OptionPositionBuilder OptionPositionBuilder = new OptionPositionBuilder(optionButton,menuButton);
        OptionPositionBuilder
                .isAlignMenuButton(false,false)
                .setWidthAndHeight(getYOptionButtonWidth(), getYOptionButtonHeight())
                .setMarginOrientation(PositionBuilder.MARGIN_LEFT,PositionBuilder.MARGIN_TOP)
                .setXYMargin(
                        (int)(centerX + x * getYOptionXMargin() - halfOptionWidth)
                        ,(int)(centerY + y * getYOptionYMargin() - halfOptionHeight)
                )
                .finish();
    }

這里OptionButton的設(shè)置和Circle8YMenu的差不多碌嘀,就是乘積因子改變了涣旨,擺成了一個(gè)樹(shù)狀的布局。動(dòng)畫(huà)的展開(kāi)也是像樹(shù)一樣分叉:

    //設(shè)置OptionButton的顯示動(dòng)畫(huà)股冗,這里是為前三個(gè)先從MenuButton冒出霹陡,后面的分別從這三個(gè)冒出
    @Override
    public Animation createOptionShowAnimation(OptionButton optionButton, int index) {
        float fromX,fromY;
        AnimationSet animationSet = new AnimationSet(true);
        if (index < 3){
            fromX = getYMenuButton().getX() - optionButton.getX();
            fromY = getYMenuButton().getY() - optionButton.getY();
        }else {
            int oldIndex = (index - 3) / 2;
            fromX = getOptionButtonList().get(oldIndex).getX() - optionButton.getX();
            fromY = getOptionButtonList().get(oldIndex).getY() - optionButton.getY();
            //設(shè)置冒出動(dòng)畫(huà)延時(shí)
            animationSet.setStartOffset(getOptionSD_AnimationDuration());
        }

        TranslateAnimation translateAnimation= new TranslateAnimation(
                fromX
                ,0
                ,fromY
                ,0);
        translateAnimation.setDuration(getOptionSD_AnimationDuration());

        AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
        alphaAnimation.setDuration(getOptionSD_AnimationDuration());

        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(translateAnimation);
        animationSet.setInterpolator(new LinearInterpolator());
        return animationSet;
    }

    //設(shè)置OptionButton的消失動(dòng)畫(huà),這里設(shè)置的是直接從當(dāng)前位置移動(dòng)到MenuButton位置消失
    @Override
    public Animation createOptionDisappearAnimation(OptionButton optionButton, int index) {

        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation translateAnimation= new TranslateAnimation(
                0
                ,getYMenuButton().getX() - optionButton.getX()
                ,0
                ,getYMenuButton().getY() - optionButton.getY()
        );
        translateAnimation.setDuration(getOptionSD_AnimationDuration());
        AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
        alphaAnimation.setDuration(getOptionSD_AnimationDuration());

        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(alphaAnimation);
        //設(shè)置動(dòng)畫(huà)延時(shí)
        animationSet.setStartOffset(60*(getOptionPositionCount() - index));
        return animationSet;
    }

這里OptionButton前三個(gè)是從MenuButton的位置冒出止状,其他的6個(gè)則是分別從這三個(gè)的位置冒出烹棉,利用延時(shí)實(shí)現(xiàn)了先后效果。而消失動(dòng)畫(huà)則是給每個(gè)OptionButton都加不同的延時(shí)怯疤,實(shí)現(xiàn)逐個(gè)回收的效果浆洗。

SquareYMenu的實(shí)現(xiàn)


  這是一個(gè)OptionButton和MenuButton組成正方形的布局,Option最大數(shù)量為8個(gè)集峦,MenuButton為位置依靠右下伏社。這里就不貼出源碼了抠刺,直接給出乘積數(shù)組,因?yàn)槠渌a思路和前面的差不多摘昌,想看具體代碼的可以看項(xiàng)目Github地址速妖。

    //8個(gè)Option位置的x、y乘積因子
    private static final int[] xTimes = {-1,-1,0,-2,-2,-1,-2,0};
    private static final int[] yTimes = {-1,0,-1,-2,-1,-2,0,-2};

Ban的補(bǔ)充

YMenuView1.x就有有Ban功能聪黎,能把一些位置設(shè)為不放置OptionButton罕容,那么YMenuView2.0也支持這個(gè),例如:

        //對(duì)Circle8YMenu
        mYMenu.setBanArray(0,2,4,6);
        //對(duì)TreeYMenu
        mYMenu.setBanArray(2,8,7);
        //對(duì)SquareYMenu
        mYMenu.setBanArray(3,4,7,5,6);

  但是動(dòng)畫(huà)延時(shí)還是會(huì)算上不被填充的位置挺举,這暫時(shí)無(wú)法避免杀赢,所以想要更好的體驗(yàn)效果的話就繼承YMenu重寫(xiě)方法吧。

總結(jié)

這次重構(gòu)過(guò)程主要使用了模板方法模式把一些次要的邏輯封裝起來(lái)湘纵,主要的類結(jié)構(gòu)從一個(gè)YMenuView擴(kuò)展為一個(gè)父類YMenu和4個(gè)子類脂崔。在這個(gè)過(guò)程中也學(xué)到了不少東西,感覺(jué)自己在這方面還是有很多不足梧喷,后面會(huì)陸續(xù)更新改進(jìn)的砌左。但是最后也把自己的想法實(shí)現(xiàn)了。在此記錄并分享铺敌,如有意見(jiàn)和建議汇歹,敬請(qǐng)?zhí)岢觥H绻矚gYMenuView的話偿凭,也可以上Github點(diǎn)個(gè)Star _产弹!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弯囊,隨后出現(xiàn)的幾起案子痰哨,更是在濱河造成了極大的恐慌,老刑警劉巖匾嘱,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斤斧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡霎烙,警方通過(guò)查閱死者的電腦和手機(jī)撬讽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悬垃,“玉大人游昼,你說(shuō)我怎么就攤上這事〕⑷洌” “怎么了酱床?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)趟佃。 經(jīng)常有香客問(wèn)我扇谣,道長(zhǎng)昧捷,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任罐寨,我火速辦了婚禮靡挥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸯绿。我一直安慰自己跋破,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布瓶蝴。 她就那樣靜靜地躺著毒返,像睡著了一般。 火紅的嫁衣襯著肌膚如雪舷手。 梳的紋絲不亂的頭發(fā)上拧簸,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音男窟,去河邊找鬼盆赤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛歉眷,可吹牛的內(nèi)容都是我干的牺六。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼汗捡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淑际!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起扇住,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庸追,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后台囱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡读整,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年簿训,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片米间。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡强品,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屈糊,到底是詐尸還是另有隱情的榛,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布逻锐,位于F島的核電站夫晌,受9級(jí)特大地震影響雕薪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晓淀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一所袁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凶掰,春花似錦燥爷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至畅涂,卻和暖如春港华,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毅戈。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工苹丸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人苇经。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓赘理,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扇单。 傳聞我的和親對(duì)象是個(gè)殘疾皇子商模,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容