最近的项目中有个比较好的开源的多个分享按钮的自定义视图,感觉比较好,所以就研究了下,写了下来。其实这个demo类似于github上开源项目ArcMenu开源项目,项目下载地址为:https://github.com/daCapricorn/ArcMenu。
实现效果图:
1、点击该按钮,五个按钮飞入屏幕;
2、点击五个按钮其中的一个后,改按钮放到直至消失,其余的按钮变小直至消失。
体验感还是挺好的。
再次点击五个按钮飞出屏幕。
好了,下面上源码吧。比较多,但都是些自定义的空间,看两遍就能看懂啦。。
只有一个布局文件:share_layout:
里面定义了布局和按钮,这些都是自定义的布局或按钮。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" > <com.customview.InOutRelativeLayout android:id="@+id/buttons_wrapper" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clipChildren="false" android:clipToPadding="false" > <!-- 微信 --> <com.customview.InOutImageButton android:id="@+id/button_weixinfriends" android:layout_width="46dip" android:layout_height="46dip" android:layout_alignParentBottom="true" android:layout_marginBottom="210dip" android:layout_marginLeft="30dip" android:background="@drawable/weixin" android:visibility="invisible" /> <!-- 微信朋友圈 --> <com.customview.InOutImageButton android:id="@+id/button_friendsQuan" android:layout_width="46dip" android:layout_height="46dip" android:layout_alignParentBottom="true" android:layout_marginBottom="190dip" android:layout_marginLeft="103dip" android:background="@drawable/friend_quan" android:visibility="invisible" /> <!-- 清除 --> <com.customview.InOutImageButton android:id="@+id/button_clear" android:layout_width="46dip" android:layout_height="46dip" android:layout_alignParentBottom="true" android:layout_marginBottom="127dip" android:layout_marginLeft="113dip" android:background="@drawable/clear" android:visibility="invisible" /> <!-- 重置 --> <com.customview.InOutImageButton android:id="@+id/button_reset" android:layout_width="46dip" android:layout_height="46dip" android:layout_alignParentBottom="true" android:layout_marginBottom="50dip" android:layout_marginLeft="97dip" android:background="@drawable/reset" android:visibility="invisible" /> <!-- 截屏 --> <com.customview.InOutImageButton android:id="@+id/button_screenshot" android:layout_width="46dip" android:layout_height="46dip" android:layout_alignParentBottom="true" android:layout_marginLeft="30dip" android:background="@drawable/screenshot" android:visibility="invisible" /> </com.customview.InOutRelativeLayout> <com.customview.InOutRelativeLayout android:id="@+id/button_control_show_hide" android:layout_width="60dip" android:layout_height="60dip" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_marginBottom="100dip" android:layout_marginLeft="20dip" android:background="@drawable/btn_selector" > <ImageView android:id="@+id/button_control_show_hide_icon" android:layout_width="60dip" android:layout_height="60dip" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_marginBottom="100dip" android:layout_marginLeft="20dip" android:visibility="visible" /> </com.customview.InOutRelativeLayout> </RelativeLayout>
布局和触发事件都是在该类中进行的
package com.view; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.widget.Toast; import com.anim.AbstractInOutAnimationSet.Direction; import com.anim.AnimationControlUtils; import com.anim.ClickAnimationSet; import com.anim.NotClickAnimationSet; import com.customview.InOutImageButton; import com.customview.InOutRelativeLayout; /** * 自定义弹入弹出按钮MainActivity * * @author zhongyao */ public class MainActivity extends ActionBarActivity { private View mControlButton; private boolean mAreButtonsShowins; private InOutRelativeLayout mButtonsWrapper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.share_layout); /** * 初始化控件 */ initshareView(); } /** * 初始化控件 */ private void initshareView() { mControlButton = findViewById(R.id.button_control_show_hide); mControlButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** * 弹出收回开关 */ toggleButton(); } }); // 所有弹出收回按钮视图集合 mButtonsWrapper = (InOutRelativeLayout) findViewById(R.id.buttons_wrapper); // 设置每个弹出收回按钮的点击事件,点击后放大并隐藏 for (int i = 0, count = mButtonsWrapper.getChildCount(); i < count; i++) { if (mButtonsWrapper.getChildAt(i) instanceof InOutImageButton) { View view = mButtonsWrapper.getChildAt(i); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View subject) { startButtonClickAnimations(subject); } }); } } } /** * 弹出收回开关 */ private void toggleButton() { if (mAreButtonsShowins) { // 按钮弹出 AnimationControlUtils.startAnimations(mButtonsWrapper, Direction.OUT, 4); } else { // 按钮弹入 AnimationControlUtils.startAnimations(mButtonsWrapper, Direction.IN, 4); } mAreButtonsShowins = !mAreButtonsShowins; } /** * 弹出收回按钮点击后动画(点击弹出按钮添加的动画) * * @param view */ private void startButtonClickAnimations(View subject) { mAreButtonsShowins = false; // 为每一个按钮都设置动画 for (int i = 0, count = mButtonsWrapper.getChildCount(); i < count; i++) { if (mButtonsWrapper.getChildAt(i) instanceof InOutImageButton) { View view = mButtonsWrapper.getChildAt(i); if (view.getId() == subject.getId()) { // 点击按钮放大并消失 ClickAnimationSet clickset = new ClickAnimationSet(600); clickset.setAnimationListener(new CustomAnimationListener( view)); view.startAnimation(clickset); } else { // 未点击按钮缩小并消失 view.startAnimation(new NotClickAnimationSet(600)); } } } } /** * 按钮点击监听器 */ private class CustomAnimationListener implements AnimationListener { private View view; public CustomAnimationListener(View view) { this.view = view; } @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { int id = view.getId(); switch (id) { case R.id.button_weixinfriends:// 分享到微信 Toast.makeText(getApplicationContext(), "分享到朋友圈", Toast.LENGTH_SHORT).show(); break; case R.id.button_friendsQuan:// 分享到朋友圈 Toast.makeText(getApplicationContext(), "分享到微信好友", Toast.LENGTH_SHORT).show(); break; case R.id.button_clear: Toast.makeText(getApplicationContext(), "清除轨迹", Toast.LENGTH_SHORT).show(); break; case R.id.button_reset: Toast.makeText(getApplicationContext(), "重置轨迹", Toast.LENGTH_SHORT).show(); break; case R.id.button_screenshot: Toast.makeText(getApplicationContext(), "截屏", Toast.LENGTH_SHORT).show(); break; default: break; } } @Override public void onAnimationRepeat(Animation animation) { } } }
package com.anim; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.AnticipateInterpolator; import android.view.animation.OvershootInterpolator; import com.anim.AbstractInOutAnimationSet.Direction; import com.customview.InOutImageButton; /** * 按钮弹入弹出工具类 * @author zhongyao */ public class AnimationControlUtils { // 动画总用时单位毫秒 private static final int DIRECTION = 500; public static void startAnimations(ViewGroup buttons, Direction directioin, int flagButton) { switch (directioin) { case IN: startAnimationsIn(buttons, flagButton); break; case OUT: startAnimationsOut(buttons, flagButton); break; } } /** * @param buttons * 所有弹出收回按钮集合 */ private static void startAnimationsIn(ViewGroup buttons, int flagButton) { final int count = buttons.getChildCount(); for (int i = 0; i < count; i++) { if (buttons.getChildAt(i) instanceof InOutImageButton) { // 为每个按钮 InOutImageButton button = (InOutImageButton) buttons .getChildAt(i); if (flagButton == 4) { // 按钮移动动画 PopupButtonAnimationSet animation = new PopupButtonAnimationSet( Direction.IN, DIRECTION, button); long offset = i * 100 / (count - 1); // 设置启动动画时间,目的是不同时移动 animation.setStartOffset(offset); // 设置图片的回弹(overshoot)效果 animation.setInterpolator(new OvershootInterpolator(2.0f)); button.startAnimation(animation); } else{ AlphaAnimation myAnimation_Alpha = new AlphaAnimation(0.0f, 1.0f); myAnimation_Alpha .setInterpolator(new AnticipateInterpolator(2.0F)); myAnimation_Alpha.setFillAfter(true); button.startAnimation(myAnimation_Alpha); } } } } /** * @param composerButtons */ private static void startAnimationsOut(ViewGroup buttons, int flagButton) { final int count = buttons.getChildCount(); for (int i = 0; i < count; i++) { if ((buttons.getChildAt(i) instanceof InOutImageButton)) { InOutImageButton button = (InOutImageButton) buttons .getChildAt(i); if (flagButton == 4) { PopupButtonAnimationSet animation = new PopupButtonAnimationSet( Direction.OUT, DIRECTION, button); long offset = (count - 1 - i) * 100 / (count - 1); animation.setStartOffset(offset); animation.setInterpolator(new AnticipateInterpolator(2.0F)); button.startAnimation(animation); } else { AlphaAnimation myAnimation_Alpha = new AlphaAnimation(1.0f, 0.0f); myAnimation_Alpha .setInterpolator(new AnticipateInterpolator(2.0F)); myAnimation_Alpha.setFillAfter(false); button.startAnimation(myAnimation_Alpha); } } } } }
package com.anim; import android.view.View; import android.view.animation.AnimationSet; /** * 按钮动画抽象类 * @author zhongyao */ public abstract class AbstractInOutAnimationSet extends AnimationSet { /** * 记录当前动画方向 */ private final Direction mDirectioin; public enum Direction { IN, OUT } public AbstractInOutAnimationSet(Direction directioin, long duration, View[] views) { super(true); mDirectioin = directioin; switch (directioin) { case IN: addInAnimation(views); break; case OUT: addOutAnimation(views); break; } // 添加完动画之后再设置执行总时间 setDuration(duration); } public Direction getDirection() { return mDirectioin; } /** * @param views */ protected abstract void addInAnimation(View[] views); /** * @param views */ protected abstract void addOutAnimation(View[] views); }
package com.anim; import android.view.View; import android.view.ViewGroup; import android.view.animation.TranslateAnimation; /** * 弹出收回按钮移动轨迹动画集合 */ public class PopupButtonAnimationSet extends AbstractInOutAnimationSet { private static final int mXOffset = 16; private static final int mYOffset = 243; public PopupButtonAnimationSet(Direction directioin, long duration, View subject) { super(directioin, duration, new View[]{ subject}); } /** 480 * 800手机上值变化规律 弹出 x = 0.0 , y = 456.0 x = -62.0 , y = 446.0 x = -118.0 , y = 418.0 x = -162.0 , y = 374.0 x = -190.0 , y = 318.0 x = -200.0 , y = 256.0 */ @Override protected void addInAnimation(View[] views) { ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) views[0].getLayoutParams(); // 计算每个弹出收回按钮的移动轨迹 float x = -layoutParams.leftMargin + mXOffset; float y = layoutParams.bottomMargin + mYOffset; // 移动位置动画 TranslateAnimation translateAnimation = new TranslateAnimation(x, 0.0f, y, 0.0f); addAnimation(translateAnimation); } /** 收回 x = 0.0 , y = 456.0 x = -62.0 , y = 446.0 x = -118.0 , y = 418.0 x = -162.0 , y = 374.0 x = -190.0 , y = 318.0 x = -200.0 , y = 256.0 */ @Override protected void addOutAnimation(View[] views) { ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) views[0].getLayoutParams(); float x = -layoutParams.leftMargin + mXOffset; float y = layoutParams.bottomMargin + mYOffset; TranslateAnimation animation = new TranslateAnimation(0.0F, x, 0.0F, y); addAnimation(animation); } }
package com.anim; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; /** * 点击按钮放大并消失动画集合 */ public class ClickAnimationSet extends AbstractInOutAnimationSet { public ClickAnimationSet(long duration) { super(Direction.OUT, duration, null); } @Override protected void addInAnimation(View[] views) { } @Override protected void addOutAnimation(View[] views) { addAnimation(new ScaleAnimation(1.0F, 5.0F, 1.0F, 5.0F, 1, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F)); addAnimation(new AlphaAnimation(1.0F, 0.0F)); } }
package com.anim; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; /** * 弹出收回按钮点击后动画 */ public class NotClickAnimationSet extends AbstractInOutAnimationSet { public NotClickAnimationSet(long duration) { // 只是控制直接调用addOutAnimation super(Direction.OUT, duration, null); } @Override protected void addInAnimation(View[] views) { } @Override protected void addOutAnimation(View[] views) { addAnimation(new ScaleAnimation(1.0F, 0.0F, 1.0F, 0.0F, Animation.RELATIVE_TO_SELF, 0.5F, 1, 0.5F)); addAnimation(new AlphaAnimation(1.0F, 0.0F)); } }
自定义布局InOutRelativeLayout:
package com.customview; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.widget.RelativeLayout; import com.anim.AbstractInOutAnimationSet; import com.anim.AbstractInOutAnimationSet.Direction; public class InOutRelativeLayout extends RelativeLayout { private Animation mAnimation; public InOutRelativeLayout(Context context) { super(context); } public InOutRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public InOutRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onAnimationStart() { super.onAnimationStart(); if (mAnimation instanceof AnimationSet) { setVisibility(View.VISIBLE); } } @Override protected void onAnimationEnd() { super.onAnimationEnd(); if (mAnimation instanceof AbstractInOutAnimationSet) { AbstractInOutAnimationSet animationSet = (AbstractInOutAnimationSet) mAnimation; if (animationSet.getDirection() == Direction.OUT) { setVisibility(View.GONE); } else { setVisibility(View.VISIBLE); } } } @Override public void startAnimation(Animation animation) { super.startAnimation(animation); mAnimation = animation; getRootView().postInvalidate(); } }
package com.customview; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.animation.Animation; import android.widget.ImageButton; import com.anim.AbstractInOutAnimationSet; /** * 弹出,收回按钮 */ public class InOutImageButton extends ImageButton { private Animation mAnimation; public InOutImageButton(Context context) { super(context); } public InOutImageButton(Context context, AttributeSet attrs) { super(context, attrs); } public InOutImageButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onAnimationStart() { super.onAnimationStart(); if (mAnimation instanceof AbstractInOutAnimationSet) { setVisibility(View.VISIBLE); } } @Override protected void onAnimationEnd() { super.onAnimationEnd(); if (mAnimation instanceof AbstractInOutAnimationSet) { AbstractInOutAnimationSet animation = (AbstractInOutAnimationSet) mAnimation; if (animation.getDirection() == com.anim.AbstractInOutAnimationSet.Direction.OUT) { setVisibility(View.GONE); } else { setVisibility(View.VISIBLE); } } } @Override public void startAnimation(Animation animation) { super.startAnimation(animation); mAnimation = animation; getRootView().postInvalidate(); } }
点击下载源码