废话不多说,先看效果:
这是菜单展开之前:
这是展开之后:
如果重复点击最左边的按钮,就会切换 "展开" 和 "收缩" 的状态。
===================================================
调用的代码如下(涉及到的图片资源,我就不上传了,这里只是说明这种效果的制作思路):
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.beautifulanimator.MyLinearLayout
android:id="@+id/ml_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
1 package com.example.beautifulanimator;
2
3 import android.os.Bundle;
4 import android.support.v7.app.AppCompatActivity;
5 import android.util.Log;
6 import android.view.View;
7
8 /**
9 *
10 */
11 public class MainActivity extends AppCompatActivity {
12
13 private MyLinearLayout myLinearLayout;
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.activity_main);
19
20 myLinearLayout = findViewById(R.id.ml_menu);
21 myLinearLayout.preset(getResources().getDrawable(R.drawable.f6, null), lis01);// 预设3个按钮,指定它的图片和事件
22 myLinearLayout.preset(getResources().getDrawable(R.drawable.f3, null), lis02);
23 myLinearLayout.preset(getResources().getDrawable(R.drawable.f4, null), lis03);
24 myLinearLayout.initLayout(this, getResources().getDrawable(R.drawable.f1, null));
25 }
26
27 View.OnClickListener lis01 = new View.OnClickListener() {
28 @Override
29 public void onClick(View v) {
30 Log.d("myLinearLayout", "click 01");
31 }
32 };
33
34 View.OnClickListener lis02 = new View.OnClickListener() {
35 @Override
36 public void onClick(View v) {
37 Log.d("myLinearLayout", "click 01");
38 }
39 };
40
41 View.OnClickListener lis03 = new View.OnClickListener() {
42 @Override
43 public void onClick(View v) {
44 Log.d("myLinearLayout", "click 01");
45 }
46 };
47
48 }
------------------------------
然后展出自定义Layout的代码:
1 package com.example.beautifulanimator;
2
3 import android.animation.Animator;
4 import android.animation.AnimatorListenerAdapter;
5 import android.animation.AnimatorSet;
6 import android.animation.ObjectAnimator;
7 import android.content.Context;
8 import android.graphics.drawable.Drawable;
9 import android.support.annotation.Nullable;
10 import android.util.AttributeSet;
11 import android.util.Log;
12 import android.view.View;
13 import android.widget.ImageView;
14 import android.widget.LinearLayout;
15
16 import java.util.ArrayList;
17 import java.util.List;
18
19 public class MyLinearLayout extends LinearLayout {
20
21 /****************************构造函数****************/
22 public MyLinearLayout(Context context) {
23 this(context, null);
24 }
25
26 public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
27 this(context, attrs, 0);
28 }
29
30 public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
31 super(context, attrs, defStyleAttr);
32 initLayoutParameters(context);
33 }
34
35 /****************************私有函数********************************/
36 private void initLayoutParameters(Context context) {
37 setOrientation(LinearLayout.HORIZONTAL);// 线性布局-横向
38 setPadding(DensityUtil.dip2px(context, 10), DensityUtil.dip2px(context, 10),
39 DensityUtil.dip2px(context, 10), DensityUtil.dip2px(context, 10));
40 }
41
42 private ImageView iv_head;// 主按钮
43 private float startDegree, endDegree;//开始旋转的角度以及结束的角度
44 private List<View> childrenMenuIconList = new ArrayList<>();// 已经绘制好的子view
45 private boolean ifMenuOpened = false;//菜单是否打开了
46 private float startX, endX;
47 private boolean ifAnimateOver = true;//动画是否执行完毕(未完毕期间不接受点击事件)
48 private int animationDuration = 300;// 动画执行的时长
49
50 /**
51 * 旋转主按钮
52 *
53 * @param iv_01
54 */
55 private void rotateMenuIcon(ImageView iv_01) {
56 if (ifMenuOpened) {
57 startDegree = 90;
58 endDegree = 0;
59 } else {
60 startDegree = 0;
61 endDegree = 90;
62 }
63
64 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv_01, "rotation", startDegree, endDegree);
65 objectAnimator.setDuration(animationDuration);
66 objectAnimator.start();
67 }
68
69 private void setIfAnimateOver(boolean temp) {
70 ifAnimateOver = temp;
71 }
72
73 /**
74 * 绘制子menu
75 */
76 private void drawChildMenuIcon(Context context, Drawable drawable, View.OnClickListener listener) {
77 setIfAnimateOver(false);
78 AnimatorSet animatorSet = new AnimatorSet();
79 final ImageView iv_menu = new ImageView(context);
80 iv_menu.setOnClickListener(listener);
81 iv_menu.setImageDrawable(drawable);
82 LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
83 LinearLayout.LayoutParams.WRAP_CONTENT);//宽高自适应
84 layoutParams.width = DensityUtil.dip2px(context, 60);
85 layoutParams.height = DensityUtil.dip2px(context, 60);
86 addView(iv_menu, layoutParams);
87 childrenMenuIconList.add(iv_menu);
88
89 //我需要的是横向的起始X和终点X
90 startX = -layoutParams.width;
91 endX = 0;
92
93 //ObjectAnimator 的 value参数,它这里有个坑,如果你是想让它慢慢显示,必须写0,1,而不能只写1.
94 //但是如果需要让它慢慢隐藏,可以只写0
95 ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(iv_menu, "alpha", 0, 1);
96 objectAnimator2.setDuration(animationDuration);
97 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv_menu, "translationX", startX, endX);
98 objectAnimator.setDuration(animationDuration);
99
100 animatorSet.playTogether(objectAnimator2, objectAnimator);//同时执行
101 animatorSet.addListener(new AnimatorListenerAdapter() {
102 @Override
103 public void onAnimationEnd(Animator animation) {
104 super.onAnimationEnd(animation);
105 setIfAnimateOver(true);
106 }
107 });
108 animatorSet.start();
109 Log.d("iv_menu", "click lis end");
110 }
111
112 private List<Animator> listAnimator = new ArrayList<>();
113
114 private void hideChildMenu() {
115 listAnimator.clear();
116 setIfAnimateOver(false);
117 for (final View v : childrenMenuIconList) {
118 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v, "translationX", endX, startX);
119 objectAnimator.setDuration(animationDuration);
120 objectAnimator.addListener(new AnimatorListenerAdapter() {
121 @Override
122 public void onAnimationEnd(Animator animation) {
123 super.onAnimationEnd(animation);
124 Log.d("hideChildMenu", "" + v.getHeight() + "-" + v.getWidth());
125 removeView(v);
126 }
127 });
128 listAnimator.add(objectAnimator);
129
130 objectAnimator = ObjectAnimator.ofFloat(v, "alpha", 0);
131 objectAnimator.setDuration(animationDuration);
132 listAnimator.add(objectAnimator);
133 }
134
135 AnimatorSet animatorSet = new AnimatorSet();
136 animatorSet.playTogether(listAnimator);
137 animatorSet.addListener(new AnimatorListenerAdapter() {
138 @Override
139 public void onAnimationEnd(Animator animation) {
140 super.onAnimationEnd(animation);
141 setIfAnimateOver(true);
142 }
143 });
144 animatorSet.start();
145 childrenMenuIconList.clear();//最后将childrenList清空
146 }
147
148 /*******************************辅助内部类***************************************/
149 private List<DrawLis> drawLisList = new ArrayList<>();//即将绘制的view的相关参数
150
151 class DrawLis {
152 Drawable drawable;
153 View.OnClickListener listener;
154
155 DrawLis(Drawable drawableT, View.OnClickListener listenerT) {
156 drawable = drawableT;
157 listener = listenerT;
158 }
159 }
160 /****************************公开接口********************************/
161 /**
162 * 新增一个按钮以及一个监听
163 *
164 * @param drawable
165 * @param listener
166 */
167 public void preset(Drawable drawable, View.OnClickListener listener) {
168 drawLisList.add(new DrawLis(drawable, listener));
169 }
170
171 public void initLayout(final Context context, Drawable drawable) {
172 iv_head = new ImageView(context);
173 iv_head.setScaleType(ImageView.ScaleType.CENTER_INSIDE);//设置等比例缩放,不会改变原图的纵横比
174 iv_head.setImageDrawable(drawable);
175 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
176 params.width = DensityUtil.dip2px(context, 60);//设置为60dp宽
177 params.height = DensityUtil.dip2px(context, 60);//设置为60dp高
178 addView(iv_head, params);//将主按钮加入布局
179
180 iv_head.setOnClickListener(new OnClickListener() {
181 @Override
182 public void onClick(View v) {
183 // 它的点击事件,点击之后,展开其他3个按钮
184 if (ifAnimateOver)//如果动画执行完毕了,那就可以进行下面的操作
185 {
186 rotateMenuIcon(iv_head);// 让主按钮产生旋转效果
187 if (!ifMenuOpened) {
188 for (DrawLis lis : drawLisList) {
189 drawChildMenuIcon(context, lis.drawable, lis.listener);//逐一添加child
190 }
191 ifMenuOpened = true;
192 } else {
193 hideChildMenu();
194 ifMenuOpened = false;
195 }
196 }
197 }
198 });
199 }
200
201 }
还有一个辅助类,用户转化dp和px:
1 package com.example.beautifulanimator; 2 3 import android.content.Context; 4 5 public class DensityUtil { 6 7 /** 8 * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 9 * 10 * @param context 11 * @param dpValue 12 * @return 13 * @date 2015年10月28日 14 */ 15 public static int dip2px(Context context, float dpValue) { 16 final float scale = context.getResources().getDisplayMetrics().density; 17 return (int) (dpValue * scale + 0.5f); 18 } 19 20 /** 21 * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 22 * 23 * @param context 24 * @param pxValue 25 * @return 26 * @date 2015年10月28日 27 */ 28 public static int px2dip(Context context, float pxValue) { 29 final float scale = context.getResources().getDisplayMetrics().density; 30 return (int) (pxValue / scale + 0.5f); 31 } 32 }
关键代码已经加了注释。欢迎各位大佬留言讨论。