Android自定义卫星菜单

前奏说明:昨天学习了一遍关于实现卫星导航菜单的博客,实现的动画使用的Tween动画,于是打算使用属性动画实现下同样的效果。
关于属性动画,属性动画通过改变view的属性来达到的动画效果,所以在本自定义view中 在动画结束后,还得开启一个新的动画换成之前的view属性


###新建自定义类继承ViewGroup

###新建自定义view中所需要的属性

res/values下面创建attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="position">
        <enum name="left_top" value="0" />
        <enum name="right_top" value="1" />
        <enum name="left_bottom" value="2" />
        <enum name="right_bottom" value="3" />
    </attr>
    <attr name="raduis" format="dimension"  />
    <declare-styleable name="ArtNavView" >
        <attr name="position"  />
        <attr name="raduis" />
    </declare-styleable>
</resources>

###layout新建自定义view布局

<?xml version="1.0" encoding="utf-8"?>
    <com.example.test.myviews.ArtNavView
	    xmlns:android="sss"      
	    xmlns:android="http://schemas.android.com/apk/res
	    /android"                                               
        xmlns:janecer="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        janecer:position="left_bottom"
        janecer:raduis="200dp"
        >
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/composer_button"
            android:padding="0dp"
            >
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/composer_icn_plus"
                />
        </FrameLayout>
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_camera"
            />
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_thought"
            />
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_music"
            />
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_place"
            />
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_sleep"
            />
    </com.example.test.myviews.ArtNavView>

###自定义类中资源获取及动画的实现
###在定义View中获取声明的自定义属性

直接上代码吧

package com.example.test.myviews;

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.ViewGroup;

import com.example.test.R;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;

/**
 * author:janecer on 2016/1/15 13:54
 * email:janecer@sina.cn
 */

public class ArtNavView extends ViewGroup implements View.OnClickListener{

    private static final String TAG = ArtNavView.class.getSimpleName()  ;

    //导航按钮
    private View mNavButton ;
    //导航菜单的位置
    private POSITION position = POSITION.LEFT_TOP ;
    //导航菜单展开的半径
    private float raduis ;
    //卫星导航菜单是否是展开
    private boolean isOpen = false ;

    //给菜单一个点击事件差的限制
    private long lastMenuItemClickTime ;
    enum POSITION{
        LEFT_TOP,RIGHT_TOP,LEFT_BOTTOM,RIGHT_BOTTOM ;
    }

    //卫星菜单被点击时的回调
    private OnMenuItemClickListenre onMenuItemClickListenre ;

    public interface OnMenuItemClickListenre {
        /**
         * @param view 被点击的菜单view
         * @param position 被点击菜单的索引下标
         */
        void onclick (View view,int position) ;
    }

    public void setOnMenuItemClickListenre(OnMenuItemClickListenre onMenuItemClickListenre) {
        this.onMenuItemClickListenre = onMenuItemClickListenre ;
    }

    public ArtNavView(Context context) {
        this(context, null );
    }

    public ArtNavView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * 当在布局文件中使用此view时,将调用此构造方法
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public ArtNavView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        raduis = TypedValue.applyDimension(TypedValue.TYPE_DIMENSION, 120, getResources().getDisplayMetrics()) ;
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArtNavView, defStyleAttr, 0) ;
        raduis = typedArray.getDimension(typedArray.getIndex(R.styleable.ArtNavView_raduis) ,raduis) ;

        switch (typedArray.getInt(typedArray.getIndex(R.styleable.ArtNavView_position), -1)) {
            case 0 :
                position = POSITION.LEFT_TOP ;
                break;
            case 1 :
                position = POSITION.RIGHT_TOP ;
                break;
            case 2 :
                position = POSITION.LEFT_BOTTOM ;
                break;
            case 3 :
                position = POSITION.RIGHT_BOTTOM ;
                break;
        }
        Log.i(TAG , "position : " + position +"  radus:" +raduis) ;
        typedArray.recycle();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int size = getChildCount() ;
        for (int i = 0 ;i<size ; i++) {
            if( i == 0){
                mNavButton = getChildAt(0) ;
                mNavButton.setOnClickListener(this);
            } else {
                final View childView = getChildAt(i) ;
                final int j = i ;
                childView .setVisibility(View.INVISIBLE);
                childView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (null != onMenuItemClickListenre) {
                            onMenuItemClickListenre.onclick(childView, j);
                        }
                        onMenuItemClickAnimation(childView,j) ;
                    }
                });
            }
            measureChild(getChildAt(i) ,widthMeasureSpec,heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){
            int size = getChildCount() ;
            View child ;
            int l1 = 0 ,t1 = 0 ;
            for (int i = 0 ;i< size  ; i++) {
                child = getChildAt(i) ;
                switch (position){
                    case LEFT_TOP:
                        l1 = 0 ;
                        t1 = 0 ;
                        break ;
                    case RIGHT_TOP:
                        l1 = getMeasuredWidth() - child.getMeasuredWidth() ;
                        t1 = 0 ;
                        break ;
                    case LEFT_BOTTOM:
                        l1 = 0 ;
                        t1 = getMeasuredHeight() - child.getMeasuredHeight() ;
                        break ;
                    case RIGHT_BOTTOM:
                        l1 = getMeasuredWidth() - child.getMeasuredWidth() ;
                        t1 = getMeasuredHeight() - child.getMeasuredHeight() ;
                        break ;
                }
                child.layout(l1, t1, l1 + child.getMeasuredWidth(), t1 + child.getMeasuredHeight());
            }
        }
    }


    @Override
    public void onClick(View v) {
        if( v == mNavButton){
            if (System.currentTimeMillis() - lastMenuItemClickTime > 300){ //压力测试,防止狂点 导致的一些问题,防止一些动画没有执行完毕又开始新的动画
                toggle(true);
            }
        }
    }

    ObjectAnimator animator1 ;
    ObjectAnimator animator2 ;
    ObjectAnimator animator3;
    public void onMenuItemClickAnimation(final View view,int position){
        int size = getChildCount() ;
        for(int i = 0 ;  i < getChildCount()  -1 ; i ++ ){
            if (i+1 == position){
                showItemClickAnim(view);
            } else {//其它menuItem直接用透明属性动画隐藏
                if(i != 0){
                    ObjectAnimator.ofFloat(getChildAt(i+1),"alpha" ,1.0f,0f).start() ;
                }
            }
        }
        lastMenuItemClickTime = System.currentTimeMillis() ;
    }

    /**
     * 点击menuItem某一项时开始的动画,
     * @param view
     */
    private void showItemClickAnim(View view) {
        animator1 = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f) ;
        animator2 = ObjectAnimator.ofFloat(view,"scaleY" ,1.0f,2.0f) ;
        animator3 = ObjectAnimator.ofFloat(view,"alpha" ,1.0f,0f) ;
        final AnimatorSet animatorSet = new AnimatorSet() ;
        animatorSet.play(animator1).with(animator2) ;
        animatorSet.play(animator2).with(animator3) ;
        animatorSet.start();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                toggle(false);
            }
        });
    }

    /**
     * 点击菜单
     */
    private void toggle(boolean isShowBackAn) {
        int l = 0 , t = 0 ;
        int size = getChildCount();
        Log.i(TAG, "clicked nav" + " size : " + size) ;
        for (int i = 0 ; i < size - 1 ; i++){
            l = (int)(raduis * Math.sin(Math.PI / 2 / (size - 2) * i)) ;
            t = (int)(raduis * Math.cos(Math.PI/2/(size - 2) * i)) ;
            startOpenOrClose(getChildAt(i+1), l, t,isShowBackAn) ;
        }
        isOpen = !isOpen ;
    }

    /**
     * 设置并且开始每个菜单的动画菜单
     * @param childAt
     * @param l
     * @param t
     * @param isShowBackAn
     */
    private void startOpenOrClose(final View childAt, int l, int t,final boolean isShowBackAn) {
        Log.i(TAG, "l : " + l + "  t : " + t) ;
        if(isShowBackAn){
            toggleNavAnimation();
        }
        ObjectAnimator obx = null ;
        ObjectAnimator oby = null ;
        ObjectAnimator obr = null ;
        ObjectAnimator obScalx =null , obScaly = null ;
        int startX = 0 ,startY = 0 ;
        switch (position){
            case LEFT_TOP:
                break ;
            case RIGHT_TOP:
                startX = getMeasuredWidth() - childAt.getMeasuredWidth() ;
                l = (getMeasuredWidth() - l - childAt.getMeasuredWidth()) ;
                break ;
            case LEFT_BOTTOM:
                startY = getMeasuredHeight() - childAt.getMeasuredHeight() ;
                t = (getMeasuredHeight() - t - childAt.getMeasuredHeight()) ;
                break ;
            case RIGHT_BOTTOM:
                l = (getMeasuredWidth() - l - childAt.getMeasuredWidth()) ;
                t = (getMeasuredHeight() - t - childAt.getMeasuredHeight()) ;
                startX = getMeasuredWidth() - childAt.getMeasuredWidth() ;
                startY = getMeasuredHeight() - childAt.getMeasuredHeight() ;
                break ;
        }
        AnimatorSet animatorSet = new AnimatorSet() ;
        if(isOpen){
            if(isShowBackAn) {
                obx = ObjectAnimator.ofFloat(childAt, "x", l, startX) ;
                oby = ObjectAnimator.ofFloat(childAt, "y", t, startY) ;
                obr = ObjectAnimator.ofFloat(childAt, "rotation", 720, 0) ;
            } else {
                obx = ObjectAnimator.ofFloat(childAt, "x",startX) ;
                obx.setDuration(10) ;
                oby = ObjectAnimator.ofFloat(childAt, "y", startY) ;
                oby.setDuration(10) ;
                obr = ObjectAnimator.ofFloat(childAt, "rotation", 720, 0) ;
                obr.setDuration(10) ;
            }
            obx.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    childAt.setVisibility(INVISIBLE);
                }
            });
        } else {
            childAt.setVisibility(VISIBLE);
            obx = ObjectAnimator.ofFloat(childAt, "x", startX, l) ;
            oby = ObjectAnimator.ofFloat(childAt, "y", startY, t) ;
            obr = ObjectAnimator.ofFloat(childAt, "rotation", 0, 720) ;
        }
        animatorSet.play(obx).with(oby) ;
        animatorSet.play(oby).with(obr) ;
        animatorSet.start();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if(!isShowBackAn){
                    AnimatorSet as = new AnimatorSet();
                    as.play(ObjectAnimator.ofFloat(childAt,"scaleX" ,1.0f)).with(ObjectAnimator.ofFloat(childAt, "scaleY", 1.0f)).with(ObjectAnimator.ofFloat(childAt, "alpha", 1.0f)) ;
                    as.setDuration(10) ;
                    as.start();
                }
            }
        });
    }

    /**
     * 点击 导航按钮开始的旋转动画
     */
    private void toggleNavAnimation(){
        int startD = 0 ;
        int endD = 0 ;
        if(isOpen){
            startD = 360 ;
            endD = 0 ;
        } else {
            startD = 0 ;
            endD = 360 ;
        }
        ObjectAnimator objectAnimator =  ObjectAnimator.ofFloat(mNavButton, "rotation", startD, endD) ;
        objectAnimator.start();
    }
}

###测试

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值