View
中读取布局文件中的自定义属性onMeasure
测量 child
即测量主按钮以及菜单项onLayout
布局 child
即布局主按钮以及菜单项menuItem
添加平移动画和旋转动画menuItem
的点击动画
二、实现
上面介绍了原理和效果图,下面来看看卫星菜单类的实现:
1.布局文件的实现
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:xcskin="http://schemas.android.com/apk/res/com.xc.xcskin" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" ><com.xc.xcskin.view.XCArcMenuViewandroid:id="@+id/arcmenu"android:layout_width="150dp"android:layout_height="150dp"android:layout_alignParentBottom="true"android:layout_alignParentLeft="true"xcskin:position="left_bottom"xcskin:radius="120dp" ><RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/composer_button" > <ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:src="@drawable/composer_icn_plus" /></RelativeLayout><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_camera"android:tag="camera" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_music"android:tag="music" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_place"android:tag="place" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_sleep"android:tag="sleep" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_thought"android:tag="thought" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_with"android:tag="with" /> </com.xc.xcskin.view.XCArcMenuView><com.xc.xcskin.view.XCArcMenuViewandroid:id="@+id/arcmenu2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"xcskin:position="right_bottom"xcskin:radius="150dp" ><RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/composer_button" > <ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:src="@drawable/composer_icn_plus" /></RelativeLayout><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_camera"android:tag="camera" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_music"android:tag="music" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_place"android:tag="place" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_sleep"android:tag="sleep" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_thought"android:tag="thought" /><ImageViewandroid:id="@+id/id_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/composer_with"android:tag="with" /> </com.xc.xcskin.view.XCArcMenuView></RelativeLayout>2.卫星菜单类的实现
package com.xc.xcskin.view;import com.xc.xcskin.R;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.view.animation.AlphaAnimation;import android.view.animation.Animation;import android.view.animation.Animation.AnimationListener;import android.view.animation.AnimationSet;import android.view.animation.RotateAnimation;import android.view.animation.ScaleAnimation;import android.view.animation.TranslateAnimation;/** * 卫星式菜单View * @author caizhiming * */public class XCArcMenuView extends ViewGroup implements OnClickListener{ private static final int POS_LEFT_TOP = 0; private static final int POS_LEFT_BOTTOM = 1; private static final int POS_RIGHT_TOP = 2; private static final int POS_RIGHT_BOTTOM = 3;private Position mPosition = Position.RIGHT_BOTTOM; private int mRadius; private Status mStatus = Status.CLOSE; //主菜的单按钮 private View mCButton; private OnMenuItemClickListener mOnMenuItemClickListener; /*** 菜单的状态枚举类* @author caizhiming**/ public enum Status{OPEN,CLOSE } /*** 菜单的位置枚举类* @author caizhiming**/ public enum Position{LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM } /*** 点击子菜单项的回调接口* @author caizhiming**/ public interface OnMenuItemClickListener {void onClick(View view, int pos); } public void setOnMenuItemClickListener( OnMenuItemClickListener onMenuItemClickListener) {this.mOnMenuItemClickListener = onMenuItemClickListener; }public XCArcMenuView(Context context) {this(context, null);// TODO Auto-generated constructor stub } public XCArcMenuView(Context context, AttributeSet attrs) {this(context, attrs, 0);// TODO Auto-generated constructor stub } public XCArcMenuView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stub//获取自定义属性TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.XCArcMenuView,defStyle,0);int pos = a.getInt(R.styleable.XCArcMenuView_position , POS_RIGHT_BOTTOM);switch (pos) { case POS_LEFT_TOP:mPosition = Position.LEFT_TOP;break; case POS_LEFT_BOTTOM:mPosition = Position.LEFT_BOTTOM;break; case POS_RIGHT_TOP:mPosition = Position.RIGHT_TOP;break; case POS_RIGHT_BOTTOM:mPosition = Position.RIGHT_BOTTOM;break;}mRadius = (int) a.getDimension(R.styleable.XCArcMenuView_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, getResources().getDisplayMetrics()));Log.v("czm", "mPosition = " + mPosition + ",mRadius = "+mRadius);a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubint count = getChildCount();for(int i = 0; i < count; i ++){ measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);}super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stubif(changed){ layoutCButton(); layoutMenuItems();} }/*** 布局主菜单项*/ private void layoutCButton() {// TODO Auto-generated method stubmCButton = getChildAt(0);mCButton.setOnClickListener(this);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; default:break;}mCButton.layout(l, t, l + width, t + height); } /*** 布局菜单项*/ private void layoutMenuItems() {// TODO Auto-generated method stubint count = getChildCount();for (int i = 0; i < count - 1; i++) { View child = getChildAt(i + 1); int l = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i)); int t = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i)); int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); // 如果菜单位置在底部 左下,右下 if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {t = getMeasuredHeight() - height - t; } // 右上,右下 if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {l = getMeasuredWidth() - width - l; } child.layout(l, t, l + width, t + height); child.setVisibility(View.GONE);} } @Override public void onClick(View v) {// TODO Auto-generated method stubmCButton = findViewById(R.id.id_button);rotateCButton(v,0,360,300);toggleMenu(300); } /*** 切换菜单*/ public void toggleMenu(int duration) {// TODO Auto-generated method stub// 为menuItem添加平移动画和旋转动画int count = getChildCount();for (int i = 0; i < count - 1; i++){ final View childView = getChildAt(i + 1); childView.setVisibility(View.VISIBLE); // end 0 , 0 // start 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 (mStatus == Status.CLOSE) {tranAnim = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);childView.setClickable(true);childView.setFocusable(true); } else // to close {tranAnim = new TranslateAnimation(0, xflag * cl, 0, yflag * ct);childView.setClickable(false);childView.setFocusable(false); } tranAnim.setFillAfter(true); tranAnim.setDuration(duration); tranAnim.setStartOffset((i * 100) / count); tranAnim.setAnimationListener(new AnimationListener() {@Overridepublic void onAnimationStart(Animation animation){}@Overridepublic void onAnimationRepeat(Animation animation){}@Overridepublic void onAnimationEnd(Animation animation){ if (mStatus == 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() {@Overridepublic void onClick(View v){ if (mOnMenuItemClickListener != null)mOnMenuItemClickListener.onClick(childView, pos); menuItemAnim(pos - 1); changeStatus();} });}// 切换菜单状态changeStatus(); } /*** 选择主菜单按钮* */ private void rotateCButton(View v, float start, float end, int duration) {// TODO Auto-generated method stubRotateAnimation 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); } /*** 添加menuItem的点击动画* */ private void menuItemAnim(int pos) {for (int i = 0; i < getChildCount() - 1; i++){ View childView = getChildAt(i + 1); if (i == pos) {childView.startAnimation(scaleBigAnim(300)); } else {childView.startAnimation(scaleSmallAnim(300)); } childView.setClickable(false); childView.setFocusable(false);} } /*** 为当前点击的Item设置变小和透明度增大的动画* @param duration* @return*/ private Animation scaleSmallAnim(int duration) {AnimationSet animationSet = new AnimationSet(true);ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);AlphaAnimation alphaAnim = new AlphaAnimation(1f, 0.0f);animationSet.addAnimation(scaleAnim);animationSet.addAnimation(alphaAnim);animationSet.setDuration(duration);animationSet.setFillAfter(true);return animationSet; } /*** 为当前点击的Item设置变大和透明度降低的动画*/ private Animation scaleBigAnim(int duration) {AnimationSet animationSet = new AnimationSet(true);ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);AlphaAnimation alphaAnim = new AlphaAnimation(1f, 0.0f);animationSet.addAnimation(scaleAnim);animationSet.addAnimation(alphaAnim);animationSet.setDuration(duration);animationSet.setFillAfter(true);return animationSet; } /*** 切换菜单状态*/ private void changeStatus() {mStatus = (mStatus == Status.CLOSE ? Status.OPEN: Status.CLOSE); } /*** 是否处于展开状态* @return*/ public boolean isOpen() {return mStatus == Status.OPEN; }}3.使用卫星式菜单类
package com.xc.xcskin;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Toast;import com.xc.xcskin.view.XCArcMenuView;import com.xc.xcskin.view.XCArcMenuView.OnMenuItemClickListener;import com.xc.xcskin.view.XCGuaguakaView;import com.xc.xcskin.view.XCGuaguakaView.OnCompleteListener;/** * 使用并测试自定义卫星式菜单View * @author caizhiming * */public class XCArcMenuViewDemo extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.xc_arcmenu_view_demo);XCArcMenuView view = (XCArcMenuView) findViewById(R.id.arcmenu);view.setOnMenuItemClickListener(new OnMenuItemClickListener() {@Override public void onClick(View view, int pos) {// TODO Auto-generated method stubString tag = (String) view.getTag();Toast.makeText(XCArcMenuViewDemo.this, tag, Toast.LENGTH_SHORT).show(); }}); }}三、总结