自定义View控件实现卫星菜单

如何自定义ViewGroup
1、自定义属性的声明与获取
2、测量onMeasure
3、布局onLayout


自定义属性的声明 与获取
1.分析需要的自定义属性
2.在res/values/attr.xml定义声明
3.在layout xml文件进行使用
4、在View的构造方法中进行获取

测量onMeasure
1.测量模式 EXACTLY、AT_MOST、UNSPECIFIED
    EXACTLY 具体的测量值
     AT_MOST 根据自身测量尺寸,至多尺寸不能父控件
    UNSPECIFIED View没有限制 要多大就可以多大


布局onLayout(ViewGroup)
1.决定View的位置
2.尽可能的将onMeasure中的一些操作移动到此方法中


自定控件代码示例:

      1)自定View控件需要,第一步首先要自定义View控件的属性,创建attr.xml

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

     2)接着在main_activity.xml中添加:

 xmlns:wwj="http://schemas.android.com/apk/res/mobi.futoubang.customview"

  <mobi.futoubang.customview.AurMenu
        android:id="@+id/main_aurMenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        wwj:position="right_bottom"
        wwj:radius="100dp">
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@mipmap/composer_button">
            <ImageView
                android:id="@+id/id_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/composer_icn_plus"/>
        </RelativeLayout>

        <ImageView
            android:tag="Music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/composer_music"/>
        <ImageView
            android:tag="Camera"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/composer_camera"/>
        <ImageView
            android:tag="Place"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/composer_place"/>
      
    </mobi.futoubang.customview.AurMenu>


     3)在自定义View类中的构造方法中获取自定义属性

   mRadius = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,100,
                getResources().getDisplayMetrics()
                );
        // 获取自定义的属性值
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.AurMenu,defStyle,0);
        int pos = a.getInt(R.styleable.AurMenu_position, 3);
        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.AurMenu_radius
        ,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,100,getResources().getDisplayMetrics()));
        Log.d("wwj", "AurMenu: "+ mPosition+"====" + mRadius);
        a.recycle();


     4)自定义View代码实现

package mobi.futoubang.customview;

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.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.RadioGroup;


public class AurMenu extends RadioGroup implements View.OnClickListener {

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


    private Position mPosition = Position.RIGHT_BOTTOM;
    private int mRadius;
    /*
    * 菜单的状态
    * */
    private Status mStatus = Status.CLOSE;
    private View mCButton;
    private OnMenuClickListener mOnMenuClickListener;



    public enum Status{
        OPEN,CLOSE
    };
    /*
    * 菜单的枚举位置
    * */
    public enum Position{
        LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
    }
    /*
    * d点击子菜单的回调接口
    * */
    public interface  OnMenuClickListener{
        void OnClick(View view,int pos);
    }

    public void setOnMenuClickListener(OnMenuClickListener onMenuClickListener) {
        mOnMenuClickListener = onMenuClickListener;
    }

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

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

    public AurMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs);
        mRadius = (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,100,
                getResources().getDisplayMetrics()
                );
        // 获取自定义的属性值
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.AurMenu,defStyle,0);
        int pos = a.getInt(R.styleable.AurMenu_position, 3);
        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.AurMenu_radius
        ,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,100,getResources().getDisplayMetrics()));
        Log.d("wwj", "AurMenu: "+ mPosition+"====" + mRadius);
        a.recycle();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed){
            layoutCButton();
            int count = getChildCount();
            for (int i = 0; i < count - 1; i++) {
                View child = getChildAt(i + 1);
                child.setVisibility(View.GONE);
                int cl = (int) (mRadius * Math.sin(Math.PI/2/(count - 2) * i));
                int ct = (int) (mRadius * Math.cos(Math.PI/2/(count - 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 layoutCButton() {
        mCButton = getChildAt(0);
        mCButton.setOnClickListener(this);
        int l= 0;
        int t = 0;

        int width = mCButton.getMeasuredWidth();
        int height = mCButton.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;
        }
        mCButton.layout(l,t,l + width,t + width);
    }
    //mCButton 的点击事件
    @Override
    public void onClick(View v) {
        rotateCButton(v,0f,360f,300);
        toggleMenu(300);
    }

    /*
    * 切换菜单
    * */
    private void toggleMenu(int duration) {
        //为MenuItem添加移动动画和旋转动画
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++) {
            final View childView = getChildAt(i + 1);

            childView.setVisibility(View.VISIBLE);
            int cl = (int) (mRadius * Math.sin(Math.PI/2/(count - 2) * i));
            int ct = (int) (mRadius * Math.cos(Math.PI/2/(count - 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);
            Animation tranAnim = null;
            // to open
            if (mStatus == Status.CLOSE) {
                tranAnim = new TranslateAnimation(xFlag * cl,0,yFlag * ct,0);
                childView.setClickable(true);
                childView.setFocusable(true);
            }else {// to close
                tranAnim = new TranslateAnimation(0,xFlag * cl,0,yFlag * ct);
                childView.setClickable(false);
                childView.setFocusable(false);
            }
            tranAnim.setFillAfter(true);
            tranAnim.setDuration(duration);
            tranAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

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

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });

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

            animationSet.addAnimation(rotateAnim);
            animationSet.addAnimation(tranAnim);

            childView.startAnimation(animationSet);
            final int pos = i + 1;
            childView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mOnMenuClickListener != null) {
                        mOnMenuClickListener.OnClick(childView,pos);
                        menuItemAnim(pos -1);
                        changeStatus();
                    }
                }
            });

        }
        // 切换菜单状态
        changeStatus();

    }

    //添加MenuItem的点击动画
    private void menuItemAnim(int pos) {
        for (int i = 0; i < getChildCount() - 1; i++) {
            View childView = getChildAt(i + 1);
            if (i == pos) {
                childView.startAnimation(scaleBigAnim(300));
            }else {
                childView.startAnimation(scaleSmallAnim(300));
            }
            childView.setClickable(false);
            childView.setFocusable(false);
        }
    }

    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(1f, 0.0f);
        animationSet.addAnimation(scaleAnimation);
        animationSet.addAnimation(alphaAnimation);

        animationSet.setDuration(duration);
        animationSet.setFillAfter(true);
        return animationSet;
    }

    /**
     * 为当前点击事件的Item设置变大和透明度度降低的动画
     * @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(1f, 0.0f);
        animationSet.addAnimation(scaleAnimation);
        animationSet.addAnimation(alphaAnimation);

        animationSet.setDuration(duration);
        animationSet.setFillAfter(true);
        return animationSet;
    }

    // 切换菜单状态
    private void changeStatus() {
        mStatus = (mStatus == Status.CLOSE ? Status.OPEN : Status.CLOSE);
    }

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

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值