看到网上一个Demo里面弹出菜单的动画效果很好看,自己就利用工作空余时间也写了一下。具体实现如下:
1 自定义一个显示Icon的ImageView,主要用来判断该ImageView是否需要放在父布局底部水平中心位置
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by crab on 14-9-26.
*/
public class BottomCenterImageView extends ImageView {
private boolean mLayoutParentBottomCenter=false;
public BottomCenterImageView(Context context) {
super(context);
}
public BottomCenterImageView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.HoriAnimationLayout,0, 0);
mLayoutParentBottomCenter=a.getBoolean(R.styleable.HoriAnimationLayout_bottomCenter,false);
a.recycle();
}
public BottomCenterImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.HoriAnimationLayout,defStyle,0);
mLayoutParentBottomCenter=a.getBoolean(R.styleable.HoriAnimationLayout_bottomCenter,false);
a.recycle();
}
public boolean isLayoutParentBottomCenter(){
return mLayoutParentBottomCenter;
}
}
2 建立一个容纳要显示ImageView的容器类
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
/**
* Created by crab on 14-9-26.
*/
public class HoriAnimationLayout extends LinearLayout{
private int mTwoLineHeight=0;
private int mOnewLineHeight=0;
//是否有一个view放在该布局的底部水平中间位置
private boolean mHasChildLayoutBottom=false;
public static final int STATE_OPEN=0;
public static final int STATE_CLOSE=1;
private static final long ANIMATION_DURATION=500L;
private int mState=STATE_CLOSE;
public HoriAnimationLayout(Context context) {
super(context);
init(context);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
BottomCenterImageView child=null;
int count=getChildCount();
for(int i=0;i<count;i++){
child= (BottomCenterImageView) getChildAt(i);
if(!child.isLayoutParentBottomCenter()){
child.setVisibility(View.INVISIBLE);
}else{
child.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
if(mState==STATE_CLOSE){
open();
}else{
close();
}
}
});
}
}
}
public HoriAnimationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public HoriAnimationLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context){
mOnewLineHeight=(int) getResources().getDimension(R.dimen.circle_icon_size);
mTwoLineHeight=(int) getResources().getDimension(R.dimen.circle_anim_parent_height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount=getChildCount();
if(childCount==0){
throw new RuntimeException("请添加一个或者更多CircleAnimationImageView");
}
int gapCount=childCount-1;
int gap=0;
int leftOffset=0;
BottomCenterImageView child=null;
if(mHasChildLayoutBottom){
gapCount=gapCount-1;
}
if(gapCount<=0){
if(gapCount==-1){
//只有一个并且需要直接布局到底部
child= (BottomCenterImageView) getChildAt(0);
child.layout((r-l)/2-child.getMeasuredWidth()/2,getMeasuredHeight()-child.getMeasuredHeight(),(r-l)/2+child.getMeasuredWidth()/2,getMeasuredHeight());
return;
}else if(gapCount==0){
gap=(r-l)/2-mOnewLineHeight/2;
}
}else{
gap=(r-l-(gapCount+1)*mOnewLineHeight)/gapCount;
}
for(int i=0;i<childCount;i++){
child= (BottomCenterImageView) getChildAt(i);
boolean isLayoutBottomCenter=child.isLayoutParentBottomCenter();
if(isLayoutBottomCenter){
child.layout((r-l)/2-child.getMeasuredWidth()/2,getMeasuredHeight()-child.getMeasuredHeight(),(r-l)/2+child.getMeasuredWidth()/2,getMeasuredHeight());
}else{
child.layout(leftOffset,0,leftOffset+child.getMeasuredWidth(),child.getMeasuredHeight());
leftOffset=leftOffset+child.getMeasuredWidth()+gap;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width=MeasureSpec.getSize(widthMeasureSpec);
int childCount=getChildCount();
for(int i=0;i<childCount;i++){
BottomCenterImageView child= (BottomCenterImageView) getChildAt(i);
if(child.isLayoutParentBottomCenter()){
mHasChildLayoutBottom=true;
}
int childMeasureSize=MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,mOnewLineHeight);
child.measure(childMeasureSize,childMeasureSize);
}
int height=0;
if(mHasChildLayoutBottom){
height=mTwoLineHeight;
}else{
height=mOnewLineHeight;
}
setMeasuredDimension(width, height);
}
private void open(){
if(mState==STATE_OPEN){
return;
}
mState=STATE_OPEN;
float dstX=getMeasuredWidth()/2-mOnewLineHeight/2;
float dstY=getMeasuredHeight()-mOnewLineHeight;
int childCount=getChildCount();
Animation[] animations=new Animation[childCount];
for(int i=0;i<childCount;i++) {
final BottomCenterImageView child = (BottomCenterImageView) getChildAt(i);
if(!child.isLayoutParentBottomCenter()) {
float fromX = dstX - child.getLeft();
float fromY = dstY - child.getTop();
float toX = 0;
float toY = 0;
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, fromX, Animation.ABSOLUTE, toX,
Animation.ABSOLUTE, fromY, Animation.ABSOLUTE, toY);
translateAnimation.setDuration(ANIMATION_DURATION);
translateAnimation.setAnimationListener(new VisbleStateAniamtionListener(child, true));
animations[i] = translateAnimation;
}else{
RotateAnimation rotateAnimation=new RotateAnimation(180,360,child.getWidth()/2,child.getHeight()/2);
rotateAnimation.setDuration(ANIMATION_DURATION);
rotateAnimation.setFillAfter(true);
animations[i]=rotateAnimation;
}
}
for(int i=0;i<childCount;i++){
final BottomCenterImageView child = (BottomCenterImageView) getChildAt(i);
child.startAnimation(animations[i]);
}
}
private void close(){
if(mState==STATE_CLOSE){
return;
}
mState=STATE_CLOSE;
float dstX=getMeasuredWidth()/2-mOnewLineHeight/2;
float dstY=getMeasuredHeight()-mOnewLineHeight;
int childCount=getChildCount();
Animation[] animations=new Animation[childCount];
for(int i=0;i<childCount;i++) {
final BottomCenterImageView child = (BottomCenterImageView) getChildAt(i);
if(!child.isLayoutParentBottomCenter()) {
float fromX = 0;
float fromY = 0;
float toX = dstX - child.getLeft();
float toY = dstY - child.getTop();
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, fromX, Animation.ABSOLUTE, toX,
Animation.ABSOLUTE, fromY, Animation.ABSOLUTE, toY);
translateAnimation.setDuration(ANIMATION_DURATION);
translateAnimation.setAnimationListener(new VisbleStateAniamtionListener(child, false));
animations[i] = translateAnimation;
}else{
RotateAnimation rotateAnimation=new RotateAnimation(0,180,child.getWidth()/2,child.getHeight()/2);
rotateAnimation.setDuration(ANIMATION_DURATION);
rotateAnimation.setFillAfter(true);
animations[i]=rotateAnimation;
}
}
for(int i=0;i<childCount;i++){
final BottomCenterImageView child = (BottomCenterImageView) getChildAt(i);
child.startAnimation(animations[i]);
}
}
private class VisbleStateAniamtionListener implements Animation.AnimationListener{
private View mView;
private boolean mVisible;
public VisbleStateAniamtionListener(View view,boolean visible){
mView=view;
mVisible=visible;
}
@Override
public void onAnimationStart(Animation animation) {
if(mView.getVisibility()!=View.VISIBLE){
mView.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animation animation) {
if(mVisible){
mView.setVisibility(View.VISIBLE);
}else{
mView.setVisibility(View.INVISIBLE);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
}
}
3定义布局文件,用到的图片资源可以自己去网上下载,最好用园行图片
<RelativeLayout
xmlns:android="/apk/res/android"
xmlns:customApp="/apk/res-auto"
android:layout_width="match_parent"
android:background="#FFCCCCCC"
android:layout_height="match_parent">
<com.example.crab.mycameratest.HoriAnimationLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
>
<com.example.crab.mycameratest.BottomCenterImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_facebook_click"
/>
<com.example.crab.mycameratest.BottomCenterImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_kupan_click"
/>
<com.example.crab.mycameratest.BottomCenterImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_sina_click"
/>
<com.example.crab.mycameratest.BottomCenterImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_twitter_click"
/>
<com.example.crab.mycameratest.BottomCenterImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_qq_click"
/>
<com.example.crab.mycameratest.BottomCenterImageView
android:id="@+id/animationTrigger"
customApp:bottomCenter="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pg_thrid_qq_click"
/>
</com.example.crab.mycameratest.HoriAnimationLayout>
</RelativeLayout>
4定义自己需要用到的一些自定义资源与样式
dimens.xml
<resources>
<dimen name="circle_icon_size">40dp</dimen>
<dimen name="circle_anim_parent_height">128dp</dimen>
</resources>
attrs.xml
<declare-styleable name="HoriAnimationLayout">
<attr name="bottomCenter" format="boolean"/>
</declare-styleable>