出處:
炎之鎧郵箱: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 _产弹!