关于做卫星式菜单的总结。
首先做这个的思路:
一、动画
主要动画为点击主按钮时主按钮的旋转,子按钮以一定半径弹射出去,子按钮弹出过程中,伴随有旋转效果。收缩后会消失。
二、自定义ViewGroup
1、自定义属性
a、attr.xml
b、在布局文件中使用
在activity中就可以通过对象直接调用监听器响应子按钮的点击事件
首先做这个的思路:
一、动画
主要动画为点击主按钮时主按钮的旋转,子按钮以一定半径弹射出去,子按钮弹出过程中,伴随有旋转效果。收缩后会消失。
总结为:
1、主按钮相应点击事件旋转。
2、子按钮弹出时旋转,收回后消失。二、自定义ViewGroup
1、自定义属性
a、attr.xml
b、在布局文件中使用
c、在自定义控件中进行读取
这一部分是为了可以以后可以修改,具体实现参照代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:alex="http://schemas.android.com/apk/res/com.example.arcmenu"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<com.example.arcmenu.view.ArcMenu
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
alex:position="right_bottom"
alex:radius="150dp" >
<ImageView
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/main"/>
..........
</com.example.arcmenu.view.ArcMenu>
</RelativeLayout>
其中第三行 对自定义资源的引用不可缺少
2、onMeasure
在自定义ViewGroup中,不需要再这个方法中作过多操作。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int count = getChildCount();
for (int i = 0; i < count; i++)
{
// 测量child
measureChild(getChildAt(i), widthMeasureSpec, widthMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
3、onLayout
在该方法中,需要对每个控件,即每个按钮进行布局
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
if (changed)
{
layoutCButton();
// 定位六个子itemmenu,以逆时针排布
int count = getChildCount();
for (int i = 0; i < count - 1; i++)
{
View child = getChildAt(i + 1);
child.setVisibility(View.GONE);
// 通过三角函数,计算位置
int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2)
* i));
int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2)
* i));
int cWidth = child.getMeasuredWidth();
int cHeight = child.getMeasuredHeight();
// 左下右下
if (mPosition == Position.LEFT_BOTTOM
|| mPosition == Position.RIGHT_BOTTOM)
{
ct = getMeasuredHeight() - cHeight - ct;
}
// 右上右下
if (mPosition == Position.RIGHT_TOP
|| mPosition == Position.RIGHT_BOTTOM)
{
cl = getMeasuredWidth() - cWidth - cl;
}
child.layout(cl, ct, cl + cWidth, ct + cHeight);
}
}
}
private void layoutCButton()
{
// 没有作有多少个子布局的判断。可以在onMeasure里面判断有多少个,不够就new一个异常
mCButton = getChildAt(0);
mCButton.setOnClickListener(this);
// 定位CButton需要他的layout(l,t,r,b)
int l = 0;
int t = 0;
int width = mCButton.getMeasuredWidth();
int height = mCButton.getMeasuredHeight();
switch (mPosition)
{
case LEFT_TOP:
l = 0;
t = 0;
break;
case LEFT_BOTTOM:
l = 0;
t = getMeasuredHeight() - height;
break;
case RIGHT_TOP:
l = getMeasuredWidth() - width;
t = 0;
break;
case RIGHT_BOTTOM:
l = getMeasuredWidth() - width;
t = getMeasuredHeight() - height;
break;
}
mCButton.layout(l, t, l + width, t + width);
}
通过计算并且对主按钮的POSITION判断,得出每个ChildView应该放置的位置。
private void rotateCButton(View v, float start, float end, int duration)
{
RotateAnimation anim = new RotateAnimation(start, end,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
anim.setDuration(duration);
anim.setFillAfter(true);
v.startAnimation(anim);
}
4、设置主按钮旋转动画 ,为menuitem设置平移动画和旋转动画。(可以在布局文件里设置ID,可以通过getChildAt(0)获取,可以在onMeasure里面设置ID(不会)。
主按钮的旋转动画和子按钮的旋转平移动画是分开写的。
主按钮的
private void rotateCButton(View v, float start, float end, int duration)
{
RotateAnimation anim = new RotateAnimation(start, end,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
anim.setDuration(duration);
anim.setFillAfter(true);
v.startAnimation(anim);
}
子按钮:
public void toggleMenu(int duration)
{
int count = getChildCount();
for (int i = 0; i < count - 1; i++)
{
final View childview = getChildAt(i + 1);
childview.setVisibility(View.VISIBLE);
Log.e("TAG1", "" + 1);
int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));
int Xflag = 1;
int Yflag = 1;
if (mPosition == Position.LEFT_TOP
|| mPosition == Position.LEFT_BOTTOM)
{
Xflag = -1;
}
if (mPosition == Position.LEFT_TOP
|| mPosition == Position.RIGHT_TOP)
{
Yflag = -1;
}
AnimationSet animset = new AnimationSet(true);
Animation tranAnim = null;
// to open
if (mCurrentStatus == Status.CLOSE)
{
tranAnim = new TranslateAnimation(Xflag * cl, 0, Yflag * ct, 0);
childview.setFocusable(true);
childview.setClickable(true);
} else
// to close
{
tranAnim = new TranslateAnimation(0, Xflag * cl, 0, Yflag * ct);
childview.setFocusable(false);
childview.setClickable(false);
}
tranAnim.setDuration(duration);
tranAnim.setFillAfter(true);
tranAnim.setStartOffset((i*100)/count);
// 一直监听,不是执行完上面才执行这段
tranAnim.setAnimationListener(new AnimationListener()
{
public void onAnimationStart(Animation animation)
{
}
public void onAnimationRepeat(Animation animation)
{
}
public void onAnimationEnd(Animation animation)
{
if (mCurrentStatus == Status.CLOSE)
{
childview.setVisibility(View.GONE);
}
}
});
RotateAnimation rotateAnim = new RotateAnimation(0, 720,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnim.setDuration(duration);
rotateAnim.setFillAfter(true);
animset.addAnimation(rotateAnim);
animset.addAnimation(tranAnim);
childview.startAnimation(animset);
final int pos = i + 1;
childview.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
if (mMenuitemClickListener != null)
mMenuitemClickListener.onClick(childview, pos);
menuItemAnim(pos - 1);
changeStatus();
}
});
}
changeStatus();
}
togglemenu是响应主按钮点击的方法。
主要内容如上。
在自定义布局中,
/**
* 点击菜单子项的回调接口
*/
public interface onMenuitemClickListener
{
void onClick(View v, int pos);
}
public void setonMenuitemClickListener(
onMenuitemClickListener mMenuitemClickListener)
{
this.mMenuitemClickListener = mMenuitemClickListener;
}
在activity中就可以通过对象直接调用监听器响应子按钮的点击事件
private ArcMenu mMenu;
...........
mMenu.setonMenuitemClickListener(new onMenuitemClickListener()
{
public void onClick(View v, int pos)
{
Toast.makeText(MainActivity.this, pos + ":" + v.getTag(),
Toast.LENGTH_SHORT).show();
}
});