浮动菜单

效果图: 

                 

1、菜单栏类

  1 /**
  2  * 1.该控件必须包含3个及以上子控件
  3  * 2.第一个子控件表示浮动菜单的母菜单,剩余的作为浮动控件的子菜单
  4  * 3.子菜单和母菜单之间的距离可以修改 RADIUS 的值
  5  */
  6 public class FloatingMenu extends ViewGroup {
  7     // 子菜单和母菜单图标距离
  8     private final static int RADIUS = 400;
  9     // 当前菜单状态
 10     private MenuStatu currentStatu = MenuStatu.STATU_CLOSE;
 11 
 12     // 菜单状态枚举
 13     public enum MenuStatu {
 14         STATU_OPEN, STATU_CLOSE
 15     }
 16 
 17     /**
 18      * 子菜单被点击回调接口
 19      */
 20     public interface OnItemMenuClickListener {
 21         void onItemMenuClick(View view, int position);
 22     }
 23     // 子菜单被点击回调接口
 24     private OnItemMenuClickListener onItemMenuClickListener;
 25     public void setOnItemMenuClickListener(OnItemMenuClickListener onItemMenuClickListener) {
 26         this.onItemMenuClickListener = onItemMenuClickListener;
 27     }
 28     public FloatingMenu(Context context) {
 29         super(context);
 30     }
 31     public FloatingMenu(Context context, AttributeSet attrs) {
 32         super(context, attrs);
 33     }
 34     public FloatingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
 35         super(context, attrs, defStyleAttr);
 36     }
 37 
 38     @Override
 39     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 40         int childCount = getChildCount();
 41 
 42         if (childCount < 3)
 43             throw new IllegalStateException("当前控件的孩子控件至少需要3个");
 44 
 45         measureChildren(widthMeasureSpec, heightMeasureSpec);
 46         int tempWidth = getChildAt(1).getMeasuredWidth();
 47         int tempHeight = getChildAt(childCount - 1).getMeasuredHeight();
 48         setMeasuredDimension(RADIUS + tempWidth, RADIUS + tempHeight);
 49     }
 50 
 51     @Override
 52     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 53         if (changed) {
 54             int measuredWidth = getMeasuredWidth();
 55             int measuredHeight = getMeasuredHeight();
 56             int childCount = getChildCount();
 57             // 计算每2个子菜单之间的角度值
 58             float averageAngle = 90 / (childCount - 1 - 1);
 59 
 60             for (int i = 0; i < childCount; i++) {
 61                 final View childAt = getChildAt(i);
 62                 int childWidth = childAt.getMeasuredWidth();
 63                 int childHeight = childAt.getMeasuredHeight();
 64 
 65                 // 第一个子控件是母菜单
 66                 if (i == 0) {
 67                     int left = measuredWidth - childWidth;
 68                     int top = measuredHeight - childHeight;
 69                     childAt.layout(left, top, measuredWidth, measuredHeight);
 70 
 71                     childAt.setOnClickListener(new OnClickListener() {
 72                         @Override
 73                         public void onClick(View v) {
 74                             changeStatuAnim(300);
 75                         }
 76                     });
 77                 } else { // 其余的为子菜单
 78                     final int temp = i;
 79                     // 计算每一个子菜单的位置
 80                     float calAngle = (i - 1) * averageAngle;
 81                     double centerX = RADIUS * Math.cos(Math.PI / 180 * calAngle);
 82                     double centerY = RADIUS * Math.sin(Math.PI / 180 * calAngle);
 83                     int left = (int) (measuredWidth - centerX - childWidth);
 84                     int top = (int) (measuredHeight - centerY - childHeight);
 85                     int right = (int) (measuredWidth - centerX);
 86                     int bottom = (int) (measuredHeight - centerY);
 87                     childAt.layout(left, top, right, bottom);
 88 
 89                     childAt.setVisibility(View.GONE);
 90 
 91                     childAt.setOnClickListener(new OnClickListener() {
 92                         @Override
 93                         public void onClick(View v) {
 94                             if (onItemMenuClickListener != null) {
 95                                 onItemMenuClickListener.onItemMenuClick(childAt, temp);
 96                             }
 97                             clickItemAnim(temp);
 98                         }
 99                     });
100                 }
101             }
102         }
103     }
104 
105     /**
106      * 点击子菜单时的动画效果
107      *
108      * @param position
109      */
110     private void clickItemAnim(int position) {
111         for (int i = 1; i < getChildCount(); i++) {
112             View childAt = getChildAt(i);
113             if (i == position) {
114                 childAt.startAnimation(toBig());
115             } else {
116                 childAt.startAnimation(toSmall());
117             }
118             childAt.setVisibility(GONE);
119         }
120         changeStatu();
121     }
122 
123     /**
124      * 变小,变透明
125      */
126     private Animation toSmall() {
127         AnimationSet animationSet = new AnimationSet(true);
128         AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
129         ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
130         animationSet.setDuration(200);
131         animationSet.addAnimation(alphaAnimation);
132         animationSet.addAnimation(scaleAnimation);
133         return animationSet;
134     }
135 
136     /**
137      * 变大,变透明
138      *
139      * @return
140      */
141     private Animation toBig() {
142         AnimationSet animationSet = new AnimationSet(true);
143         AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
144         ScaleAnimation scaleAnimation = new ScaleAnimation(1, 3, 1, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
145         animationSet.setDuration(200);
146         animationSet.addAnimation(alphaAnimation);
147         animationSet.addAnimation(scaleAnimation);
148         return animationSet;
149     }
150 
151     /**
152      * 子菜单状态改变动画效果
153      *
154      * @param durationMillis 动画执行时间
155      */
156     private void changeStatuAnim(int durationMillis) {
157         int childCount = getChildCount();
158         for (int i = 1; i < childCount; i++) {
159             final View view = getChildAt(i);
160 
161             float jiao = 90 / (childCount - 1 - 1);
162             float jiJiao = (i - 1) * jiao;
163             float toX = (float) (RADIUS * Math.cos(Math.PI / 180 * jiJiao));
164             float toY = (float) (RADIUS * Math.sin(Math.PI / 180 * jiJiao));
165 
166             AnimationSet animationSet = new AnimationSet(true);
167             RotateAnimation rotateAnim = new RotateAnimation(
168                     0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
169             TranslateAnimation translateAnimation;
170             AlphaAnimation alphaAnimation;
171             // 根据子菜单状态实现不同的动画效果
172             if (!isOpen()) {
173                 translateAnimation = new TranslateAnimation(toX, 0, toY, 0);
174                 alphaAnimation = new AlphaAnimation(0, 1);
175             } else {
176                 translateAnimation = new TranslateAnimation(0, toX, 0, toY);
177                 alphaAnimation = new AlphaAnimation(1, 0);
178             }
179 
180             // 注意先添加旋转动画,再添加平移动画
181             animationSet.addAnimation(rotateAnim);
182             animationSet.addAnimation(translateAnimation);
183             animationSet.addAnimation(alphaAnimation);
184             animationSet.setDuration(durationMillis);
185             animationSet.setAnimationListener(new Animation.AnimationListener() {
186                 @Override
187                 public void onAnimationStart(Animation animation) {
188 
189                 }
190 
191                 @Override
192                 public void onAnimationEnd(Animation animation) {
193                     if (currentStatu == MenuStatu.STATU_OPEN) {
194                         view.setVisibility(View.VISIBLE);
195                     } else {
196                         view.setVisibility(View.GONE);
197                     }
198                 }
199 
200                 @Override
201                 public void onAnimationRepeat(Animation animation) {
202 
203                 }
204             });
205             view.startAnimation(animationSet);
206         }
207         changeStatu();
208     }
209 
210     /**
211      * 改变状态
212      */
213     private void changeStatu() {
214         currentStatu = (currentStatu == MenuStatu.STATU_OPEN) ? MenuStatu.STATU_CLOSE : MenuStatu.STATU_OPEN;
215     }
216 
217     /**
218      * 对外暴露的方法,关闭子菜单
219      */
220     public void closeMenu() {
221         changeStatuAnim(300);
222     }
223 
224     /**
225      * 判断是否打开子菜单
226      *
227      * @return
228      */
229     public boolean isOpen() {
230         return currentStatu == MenuStatu.STATU_OPEN;
231     }
232 }

 2、布局文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <com.example.dell.floatmenudemo.weight.FloatingMenu
 7         android:id="@+id/floating"
 8         android:layout_width="wrap_content"
 9         android:layout_height="wrap_content"
10         android:layout_alignParentBottom="true"
11         android:layout_alignParentRight="true"
12         android:layout_marginBottom="50dp"
13         android:layout_marginRight="50dp">
14 
15         <!--第一个子控件表示母菜单-->
16         <ImageView
17             android:id="@+id/imageViewSwitch"
18             android:layout_width="wrap_content"
19             android:layout_height="wrap_content"
20             android:layout_alignParentLeft="true"
21             android:layout_alignParentStart="true"
22             android:layout_alignParentTop="true"
23             android:src="@mipmap/ic_launcher" />
24 
25         <!--其余的控件表示子菜单-->
26         <ImageView
27             android:layout_width="wrap_content"
28             android:layout_height="wrap_content"
29             android:src="@mipmap/ic_launcher_round" />
30 
31         <ImageView
32             android:layout_width="wrap_content"
33             android:layout_height="wrap_content"
34             android:src="@mipmap/ic_launcher_round" />
35 
36         <ImageView
37             android:layout_width="wrap_content"
38             android:layout_height="wrap_content"
39             android:src="@mipmap/ic_launcher_round" />
40 
41         <ImageView
42             android:layout_width="wrap_content"
43             android:layout_height="wrap_content"
44             android:src="@mipmap/ic_launcher_round" />
45 
46         <ImageView
47             android:layout_width="wrap_content"
48             android:layout_height="wrap_content"
49             android:src="@mipmap/ic_launcher_round" />
50 
51         <ImageView
52             android:layout_width="wrap_content"
53             android:layout_height="wrap_content"
54             android:src="@mipmap/ic_launcher_round" />
55     </com.example.dell.floatmenudemo.weight.FloatingMenu>
56 
57 </RelativeLayout>

 3、 activity

 1 package com.example.dell.floatmenudemo;
 2 
 3 import android.os.Bundle;
 4 import android.support.v7.app.AppCompatActivity;
 5 import android.view.View;
 6 import android.widget.Toast;
 7 
 8 import com.example.dell.floatmenudemo.weight.FloatingMenu;
 9 public class MainActivity extends AppCompatActivity {
10     private FloatingMenu floating;
11     @Override
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_main);
15         floating = (FloatingMenu) findViewById(R.id.floating);
16         initFloatingMenu();
17     }
18     /**
19      * 初始化浮动菜单控件
20      */
21     private void initFloatingMenu() {
22         floating.setOnItemMenuClickListener(new FloatingMenu.OnItemMenuClickListener() {
23             @Override
24             public void onItemMenuClick(View view, int position) {
25                 Toast.makeText(MainActivity.this, "子菜单 - " + position, Toast.LENGTH_SHORT).show();
26             }
27         });
28     }
29 }

 4、 还可以编写 横竖的菜单栏 ,效果如下:

                                            

代码如下:

  1 package com.example.dell.floatmenudemo.weight;
  2 
  3 import android.content.Context;
  4 import android.graphics.Camera;
  5 import android.util.AttributeSet;
  6 import android.util.Log;
  7 import android.view.View;
  8 import android.view.ViewGroup;
  9 import android.view.animation.AlphaAnimation;
 10 import android.view.animation.Animation;
 11 import android.view.animation.AnimationSet;
 12 import android.view.animation.RotateAnimation;
 13 import android.view.animation.ScaleAnimation;
 14 import android.view.animation.TranslateAnimation;
 15 
 16 public  class  FloatingActionsMenu extends ViewGroup{
 17     private static final String TAG = "FloatingActionsMenu";
 18  //  子控件之间的距离
 19 public   final  static  int DISTANCE = 20;
 20  // 枚举 菜单栏的状态
 21 public  enum  MenuStatus{STATUS_OPEN,STATUS_CLOSE}
 22 // 当前状态
 23 private  MenuStatus currentStatus = MenuStatus.STATUS_CLOSE;
 24 // 子菜单点击回调接口
 25     public interface  OnItemMenuClickListener{
 26         void  onItemMenuClick(View view,int position);// 此方法内对点击作出响应,需实例化
 27 }
 28 //  调用接口封装到方法
 29     private  OnItemMenuClickListener onItemMenuClickListener;
 30     public void setOnItemMenuClickListener(OnItemMenuClickListener onItemMenuClickListener){
 31         this.onItemMenuClickListener = onItemMenuClickListener;
 32     }
 33     public FloatingActionsMenu(Context context) {
 34         super(context);
 35     }
 36     public FloatingActionsMenu(Context context, AttributeSet attrs) {
 37         super(context, attrs);
 38     }
 39 
 40         //计算 menu 大小
 41     @Override
 42     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 43             int childCount = getChildCount();
 44             if (childCount<3)throw  new IllegalStateException("当前控件至少需要三个子控件");
 45             measureChildren(widthMeasureSpec,heightMeasureSpec);
 46             int tempWidth = getChildAt(0).getMeasuredWidth();
 47             int tempHeight = getChildAt(0).getMeasuredHeight();
 48 
 49             setMeasuredDimension(tempWidth,(tempHeight+DISTANCE)*childCount);
 50     }
 51     // 计算控件位置
 52     //     changed   是否可以改变
 53     @Override
 54     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 55         if(changed){
 56             int measureWidth  = getMeasuredWidth();
 57             int measureHeidth  = getMeasuredHeight();
 58             int childCount = getChildCount();
 59             for(int i = 0 ;i<childCount;i++){
 60                 final  View childAt = getChildAt(i);
 61                 int childWidth = childAt.getMeasuredWidth();
 62                 int childHeight = childAt.getMeasuredHeight();
 63                 // 第一个子控件是菜单栏开关
 64                 if (i==0){
 65                     int left = 0;
 66                     int top = (childCount-1-i)*(DISTANCE+childHeight);
 67                     int right = left+childWidth;
 68                     int bottom = top+childHeight;
 69                 childAt.layout(left,top,right,bottom);
 70                 childAt.setOnClickListener(new OnClickListener() {
 71                     @Override
 72                     public void onClick(View view) {
 73                         changeStatuAnim(300);
 74                     }
 75                 });
 76                 }else {
 77                     // 其余的为 子控件按钮
 78                     final  int temp = i;
 79                     int left = 0;
 80                     int top = (childCount-1-i)*(DISTANCE+childHeight);
 81                     int right = left+childWidth;
 82                     int bottom = top+childHeight;
 83                     childAt.layout(left,top,right,bottom);
 84                     childAt.setVisibility(View.GONE);
 85                     childAt.setOnClickListener(new OnClickListener() {
 86                         @Override
 87                         public void onClick(View view) {
 88                             if (onItemMenuClickListener!=null){
 89                                 onItemMenuClickListener.onItemMenuClick(childAt,temp);
 90                             }
 91                             clickItemAnim(temp);
 92                         }
 93                     });
 94                 }
 95             }
 96         }
 97     }
 98 
 99     /**
100      *    点击子菜单时的动画效果
101      */
102     private void clickItemAnim(int position){
103         for (int i=1;i<getChildCount();i++){
104             View childAt = getChildAt(i);
105             if (i==position){
106 //                childAt.startAnimation(toBig());
107                 childAt.startAnimation(rotateAroundY());// Y轴旋转
108             }
109             childAt.setVisibility(GONE);
110         }
111         changeStatu();
112     }
113 
114     /**
115      *   变小,变透明
116      * @return
117      */
118     private Animation toSmall(){
119         AnimationSet animationSet = new AnimationSet(true);
120         AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);// 透明
121         animationSet.setDuration(200);// 200 毫秒
122         animationSet.addAnimation(alphaAnimation);
123         return  animationSet;
124     }
125     /**
126      *    变大,变透明
127      */
128     private  Animation toBig(){
129         AnimationSet animationSet = new AnimationSet(true);
130         AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);// 透明
131         animationSet.addAnimation(alphaAnimation);
132         ScaleAnimation scaleAnimation = new ScaleAnimation(1,3,1,3,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);// 变大
133         animationSet.setDuration(200);// 200 毫秒
134         return  animationSet;
135     }
136     /**
137      *  绕Y 轴旋转
138      */
139     private  Animation rotateAroundY(){
140           RotateToYAnimation rotateToYAnimation = new RotateToYAnimation();
141           rotateToYAnimation.setRepeatCount(1);
142           return  rotateToYAnimation;
143     }
144     /**
145      *
146      *    子菜单状态改变动画效果
147      */
148     private  void changeStatuAnim(int durationMillis){
149         int childCount = getChildCount();
150         for (int i = 0;i<childCount;i++){
151             final View view = getChildAt(i);
152             int toX = 0;
153             int toY = (i-1)*view.getHeight()+DISTANCE*i;
154             if (i==0){
155                 //  do something
156             }else {
157 
158                 AnimationSet animationSet = new AnimationSet(true);
159                 animationSet.setDuration(300);
160                 RotateAnimation rotateAnimation = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
161                 TranslateAnimation translateAnimation;
162                 AlphaAnimation alphaAnimation;
163                 // 根据不同的子菜单状态实现不同的效果
164                 if (!isopen()){
165                     translateAnimation = new TranslateAnimation(toX,0,toY,0);
166                     alphaAnimation = new AlphaAnimation(0,1);
167 //                    currentStatus = MenuStatus.STATUS_OPEN;
168                 }else {
169                     translateAnimation = new TranslateAnimation(0,toX,0,toY);
170                     alphaAnimation = new AlphaAnimation(1,0);
171 //                    currentStatus = MenuStatus.STATUS_CLOSE;
172 
173                 }
174                 //  先添加旋转 在添加 平移
175                 animationSet.addAnimation(rotateAnimation);
176                 animationSet.addAnimation(translateAnimation);
177                 animationSet.addAnimation(alphaAnimation);
178                 animationSet.setAnimationListener(new Animation.AnimationListener() {
179                     @Override
180                     public void onAnimationStart(Animation animation) {
181 
182                     }
183 
184                     @Override
185                     public void onAnimationEnd(Animation animation) {
186                         if (currentStatus == MenuStatus.STATUS_OPEN){
187                             view.setVisibility(VISIBLE);
188                         }else {
189 
190                             view.setVisibility(View.GONE);
191                         }
192                     }
193                     @Override
194                     public void onAnimationRepeat(Animation animation) {
195 
196                     }
197                 });
198                 view.startAnimation(animationSet);
199             }
200         }
201         changeStatu();
202     }
203 /**
204  *   改变状态
205  */
206     private  void  changeStatu(){
207         currentStatus = (currentStatus== MenuStatus.STATUS_OPEN)?MenuStatus.STATUS_CLOSE:MenuStatus.STATUS_OPEN;
208     }
209 
210     /**
211      *   对外暴露的方法  关闭子菜单
212      */
213 
214     public  void  closeMenu(){changeStatuAnim(300);}
215     /**
216      * 判断是否打开  子菜单
217      */
218     public  boolean isopen(){return  currentStatus==MenuStatus.STATUS_OPEN;}
 }

其中用到 沿Y轴为轴旋转的动画,代码如下:

    

 1 package com.example.dell.floatmenudemo.weight;
 2 
 3 import android.graphics.Camera;
 4 import android.graphics.Matrix;
 5 import android.view.animation.Animation;
 6 import android.view.animation.DecelerateInterpolator;
 7 import android.view.animation.Transformation;
 8 
 9 /**
10 
11  调用代码
12  RotateToYAnimation  rotateToYAnimation = new RotateToYAnimation();
13  rotateToYAnimation.setRepeatCount(Animation.INFINITE); //翻转无数次
14  view.startAnimation(rotateToYAnimation);
15 
16  */
17 
18 public class RotateToYAnimation extends Animation{
19 private Camera camera = new Camera();
20 private int centerX ;
21 private int centerY;
22 
23     /**
24      *   获取坐标  定义动画时间
25      * @param width
26      * @param height
27      * @param parentWidth
28      * @param parentHeight
29      */
30     @Override
31     public void initialize(int width, int height, int parentWidth, int parentHeight) {
32         super.initialize(width, height, parentWidth, parentHeight);
33         // 获取中心点坐标
34         centerX = width/2;
35         centerY = width/2;
36         //  动画执行时间
37         setDuration(200);
38         // 内插器
39         setInterpolator(new DecelerateInterpolator());
40     }
41 
42     /**
43      *
44      *    旋转角度
45      * @param interpolatedTime
46      * @param t
47      */
48     @Override
49     protected void applyTransformation(float interpolatedTime, Transformation t) {
50             final Matrix matrix = t.getMatrix();
51             camera.save();
52             //  旋转中心是Y轴  可自行设置
53             camera.rotateY(360*interpolatedTime);
54             //  把 摄像头  加在变换 矩阵上
55         camera.getMatrix(matrix);
56         //  设置翻转  中心点
57         matrix.preTranslate(-centerX,centerY);
58         matrix.postTranslate(centerX,centerY);
59         camera.restore();
60     }
61 }

猜你喜欢

转载自www.cnblogs.com/the-wang/p/9061346.html