自定义ViewGroup之卫星菜单

知识点
一、动画

二、自定义ViewGroup
1、自定义属性
a、attrs.xml
b、在布局文件中使用
c、在自定义控件种进行读取
2、onMeasure()
3、onLayout()
4、设置主Button旋转动画
5、为menuItem添加平移动画和旋转动画


效果
这里写图片描述


ArcMenu

package com.example.day0228;

import android.R.animator;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;

public class ArcMenu extends ViewGroup implements OnClickListener{

    private final int POS_LEFT_TOP = 0;
    private final int POS_LEFT_BOTTOM = 1;
    private final int POS_RIGHT_TOP = 2;
    private final int POS_RIGHT_BOTTOM = 3;

    private Position mPosition;
    private int mRadius;
    private Status mStatus = Status.CLOSE;
    private OnMenuItemClickListener itemClickListener;
    /**
     * 菜单的主按钮
     */
    private View mButton;

    /**
     * 菜单的状态
     */
    public enum Status 
    {
        OPEN,CLOSE
    }
    /**
     * 菜单的位置枚举类
     */
    public enum Position
    {
        LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
    }

    public void setOnMenuItemClickListener(OnMenuItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }

    public ArcMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //默认
        mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
        mPosition = Position.RIGHT_BOTTOM;

        //获取自定义属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyleAttr, 0);
        int pos = a.getInt(R.styleable.ArcMenu_position, POS_RIGHT_BOTTOM);
        switch (pos) {
        case POS_LEFT_TOP:
            mPosition = Position.LEFT_TOP;
            break;
        case POS_LEFT_BOTTOM:
            mPosition = Position.LEFT_BOTTOM;
            break;
        case POS_RIGHT_TOP:
            mPosition = Position.RIGHT_TOP;
            break;
        case POS_RIGHT_BOTTOM:
            mPosition = Position.RIGHT_BOTTOM;
            break;
            }
        mRadius = (int) a.getDimension(R.styleable.ArcMenu_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()));
        a.recycle();

    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int cCount = getChildCount();
        for (int i = 0; i < cCount; i++) {
            //测量child
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            layoutButton();
            layoutItem();
        }

    }

    /**
     * 定位子项
     */
    private void layoutItem() {
        int cCount = getChildCount();

        for (int i = 0; i < cCount - 1; i++) {
            View child = getChildAt(i+1);

            //隐藏
            child.setVisibility(View.GONE);

            int cl = (int) (mRadius * Math.sin(Math.PI/2/(cCount-2)*i));
            int ct = (int) (mRadius * Math.cos(Math.PI/2/(cCount-2)*i));

            int cWidth = child.getMeasuredWidth();
            int cHeight = child.getMeasuredHeight();

            //如果菜单位置在底部
            //左下,右下
            if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {
                ct = getMeasuredHeight() - cHeight -ct;
            }
            //右上,右下
            if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {
                cl = getMeasuredWidth() - cWidth - cl;
            }

            child.layout(cl, ct, cl + cWidth, ct + cHeight);
        }

    }

    /**
     * 定位主菜单按钮
     */
    private void layoutButton() {
        mButton = getChildAt(0);
        mButton.setOnClickListener(this);

        int l = 0;
        int t = 0;

        int width = mButton.getMeasuredWidth();
        int height = mButton.getMeasuredHeight();

        switch (mPosition) {
        case LEFT_TOP:
            l = 0;
            t = 0;
            break;
        case LEFT_BOTTOM:
            l = 0;
            t = getMeasuredHeight()- height;
            break;
        case RIGHT_TOP:
            l = getMeasuredWidth() - width;
            t = 0;
            break;
        case RIGHT_BOTTOM:
            l = getMeasuredWidth() - width;
            t = getMeasuredHeight()- height;
            break;
        }

        mButton.layout(l, t, l + width, t + height);

    }

    /**
     *点击主菜单的回调接口 
     */
    public interface OnMenuItemClickListener {
        void onClick(View view,int position);
    }

    @Override
    public void onClick(View v) {

        if (v==getChildAt(0)) {
            rotateView(v,0f,360f ,1000);
            toggleMenu(400);
        }
//      switch (v.getId()) {
//      case R.id.m_button:
//          rotateView(v,0f,360f ,1000);
//          toggleMenu(1000);
//          break;
//      }

    }

    /**
     * 切换菜单
     */
    private void toggleMenu(int duration) {
        //为menuItem添加平移动画和旋转动画
        int cCount = getChildCount();
        for (int i = 0;i < cCount - 1; i++) {
            final View child = getChildAt(i + 1);
            //要显示出来才有动画
            child.setVisibility(View.VISIBLE);
            // end 0 0;
            // start
            int cl = (int) (mRadius * Math.sin(Math.PI/2/(cCount-2)*i));
            int ct = (int) (mRadius * Math.cos(Math.PI/2/(cCount-2)*i));

            int xFlag = 1;
            int yFlag = 1;

            if (mPosition == Position.LEFT_TOP || mPosition == Position.LEFT_BOTTOM) {
                xFlag = -1;
            }

            if (mPosition == Position.LEFT_TOP || mPosition == Position.RIGHT_TOP) {
                yFlag = -1;
            }

            AnimationSet animationSet = new AnimationSet(true);
            animationSet.setDuration(duration);
            Animation animation = null;

            //to open
            if (mStatus == Status.CLOSE) {
                animation = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0);
                child.setClickable(true);
                child.setFocusable(true);
            } else { //to close
                animation = new TranslateAnimation(0, xFlag * cl, 0, yFlag * ct);
                child.setClickable(false);
                child.setFocusable(false);
            }
            animation.setStartOffset(200*(i+1)/cCount);
            animation.setFillAfter(true);
            animation.setDuration(duration);
            animation.setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    if (mStatus == Status.CLOSE) {
                        child.setVisibility(View.GONE);
                    }
                }
            });

            //旋转动画
            Animation rotateAnim = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
            animation.setDuration(duration);
            animation.setFillAfter(true);

            animationSet.addAnimation(rotateAnim);
            animationSet.addAnimation(animation);

            child.startAnimation(animationSet);
            final int pos = i+1;
            child.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (itemClickListener != null) {
                        itemClickListener.onClick(child, pos);
                    }

                    menuItemAnim(pos-1);
                    changeState();
                }
            });
        }

        //切换菜单状态
        changeState();

    }

    /**
     * 添加menuItem的点击动画
     * @param pos
     */
    protected void menuItemAnim(int pos) {
        for (int i = 0;i < getChildCount()-1 ; i++) {
            View child = getChildAt(i+1);
            if (i == pos) {
                //变大动画
                child.startAnimation(scaleBigAnim(300));
            } else {
                //变小动画
                child.startAnimation(scaleSmallAnim(300));
            }

            child.setClickable(false);
            child.setFocusable(false);
        }

    }

    /**
     * 变大和变透明动画
     * @param duration
     * @return
     */
    private Animation scaleBigAnim(int duration) {
        AnimationSet animationSet = new AnimationSet(true);
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
        animationSet.addAnimation(scaleAnimation);
        animationSet.addAnimation(alphaAnimation);
        animationSet.setDuration(duration);
        animationSet.setFillAfter(true);
        return animationSet;
    }

    /**
     * 变小和变透明动画
     * @param duration
     * @return
     */
    private Animation scaleSmallAnim(int duration) {
        AnimationSet animationSet = new AnimationSet(true);
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(scaleAnimation);
        animationSet.setDuration(duration);
        animationSet.setFillAfter(true);
        return animationSet;
    }



    private void changeState() {
        mStatus = (mStatus == Status.CLOSE)? Status.OPEN : Status.CLOSE;

    }

    private void rotateView(View v, float start, float end, int duration) {
        Animation animation = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
        animation.setDuration(duration);
        animation.setFillAfter(true);
        v.startAnimation(animation);
    }


}

attrs.xml

<resources>

    <!-- 位置 -->
    <attr name="position">
        <enum name="left_top" value="0"></enum>
        <enum name="left_bottom" value="1"></enum>
        <enum name="right_top" value="2"></enum>
        <enum name="right_bottom" value="3"></enum>
    </attr>
    <!-- 半径 -->
    <attr name="radius" format="dimension"></attr>

    <declare-styleable name="ArcMenu">
        <!-- 声明属性 -->
        <attr name="position"></attr>
        <attr name="radius"></attr>
    </declare-styleable>

</resources>

Main.xml

<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"
    xmlns:app="http://schemas.android.com/apk/res/com.example.day0228">

    <com.example.day0228.ArcMenu 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:position="left_top"
        app:radius="140dp"
        >

        <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/composer_button">
           <ImageView 
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@drawable/composer_icn_plus"
              android:layout_centerInParent="true" />
        </RelativeLayout>

        <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_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"
            />
                <ImageView 
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
              android:src="@drawable/composer_with"
            />



    </com.example.day0228.ArcMenu>

     <com.example.day0228.ArcMenu 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:position="left_bottom"
        app:radius="140dp"
        >

        <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/composer_button">
           <ImageView 
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@drawable/composer_icn_plus"
              android:layout_centerInParent="true" />
        </RelativeLayout>

        <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_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"
            />
                <ImageView 
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
              android:src="@drawable/composer_with"
            />



    </com.example.day0228.ArcMenu>

      <com.example.day0228.ArcMenu 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:position="right_bottom"
        app:radius="100dp"
        >

        <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/composer_button">
           <ImageView 
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@drawable/composer_icn_plus"
              android:layout_centerInParent="true" />
        </RelativeLayout>

        <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_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"
            />
                <ImageView 
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
              android:src="@drawable/composer_with"
            />



    </com.example.day0228.ArcMenu>

     <com.example.day0228.ArcMenu 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:position="right_top"
        app:radius="100dp"
        >

        <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/composer_button">
           <ImageView 
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:src="@drawable/composer_icn_plus"
              android:layout_centerInParent="true" />
        </RelativeLayout>

        <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_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"
            />
                <ImageView 
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
              android:src="@drawable/composer_with"
            />



    </com.example.day0228.ArcMenu>





</RelativeLayout>

转载于:http://www.imooc.com/learn/300

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值