android自定义ViewGroup卫星导航菜单

来源:互联网 发布:ubuntu u盘 编辑:程序博客网 时间:2024/04/30 12:29

卫星导航菜单看起来还是很酷的,实现起来也不是很难,网上也已经有很多这样的demo,而且封装的也特别好。作为一个技术gou,不能只是一味的用现成的轮子,向大神学习,自己造轮子。今天菜鸟也来尝试一下,从头到尾来实现一吧这高端的ui效果。。
老规矩,上图上代码:

这里写图片描述

效果还可以吧,也许有的人会认为有点low,但是我想说的是晚上关上灯效果是一样的,重点是内涵。。。

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="radius" format="dimension" />    <attr name="CustomLayout">        <enum name="left_top" value="0" />        <enum name="right_top" value="1" />        <enum name="right_bottom" value="2" />        <enum name="left_bottom" value="3" />        <enum name="bottom_center" value="4" />    </attr>    <attr name="MenuStats">        <enum name="open" value="0" />        <enum name="close" value="1" />    </attr>    <declare-styleable name="PlanetViewGroup">        <attr name="radius" />        <attr name="CustomLayout" />        <attr name="MenuStats" />    </declare-styleable></resources>

用到了enum,主要就是指定用户选择的范围,不让用户乱搞。。。

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.example.apple.Custom.PlanetViewGroup        android:id="@+id/planet_top"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:CustomLayout="bottom_center"        app:MenuStats="open"        app:radius="130dp">        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_red_light"            android:src="@mipmap/ic_launcher"            android:text="1" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_blue_dark"            android:src="@mipmap/ic_launcher"            android:text="2" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_green_light"            android:src="@mipmap/ic_launcher"            android:text="3" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_orange_dark"            android:src="@mipmap/ic_launcher"            android:text="4" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_blue_light"            android:src="@mipmap/ic_launcher"            android:text="5" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_orange_dark"            android:src="@mipmap/ic_launcher"            android:text="6" />        <TextView            android:layout_width="30dp"            android:layout_height="30dp"            android:background="@android:color/holo_red_dark"            android:src="@mipmap/ic_launcher"            android:text="C" />    </com.example.apple.Custom.PlanetViewGroup>    <com.example.apple.Custom.PlanetViewGroup        android:id="@+id/planet_bottem"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:CustomLayout="left_top"        app:MenuStats="open"        app:radius="130dp">        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_red_light"            android:src="@mipmap/ic_launcher"            android:text="1" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_blue_dark"            android:src="@mipmap/ic_launcher"            android:text="2" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_green_light"            android:src="@mipmap/ic_launcher"            android:text="3" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_orange_dark"            android:src="@mipmap/ic_launcher"            android:text="4" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_blue_light"            android:src="@mipmap/ic_launcher"            android:text="5" />        <TextView            android:layout_width="20dp"            android:layout_height="20dp"            android:background="@android:color/holo_orange_dark"            android:src="@mipmap/ic_launcher"            android:text="6" />        <TextView            android:layout_width="30dp"            android:layout_height="30dp"            android:background="@android:color/holo_red_dark"            android:src="@mipmap/ic_launcher"            android:text="C" />    </com.example.apple.Custom.PlanetViewGroup></RelativeLayout>

使用起来也是比较简单的,可以在布局里面指定菜单的位置,一般就四个角,和底部的中间位置,这里都是支持的 。还可以指定,子菜单是关闭还是显示,open close.

package com.example.apple.Custom;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import android.view.animation.OvershootInterpolator;import com.example.apple.pullzoom.R;import com.nineoldandroids.animation.Animator;import com.nineoldandroids.animation.AnimatorSet;import com.nineoldandroids.animation.ObjectAnimator;/** * Created by apple on 17/9/21. */public class PlanetViewGroup extends ViewGroup implements View.OnClickListener {    final String TAG = this.getClass().getSimpleName();    CustomLayout mlayout = CustomLayout.left_top;    MenuStats menuStats = MenuStats.open;    private int count = 0;    View centerView;    MenuOnClickListener lis;    int cl, ct, cr, cb, childWidth, childHeight;    /**     * 半径     */    private int radius = 100;    private double angle;    public enum CustomLayout {        left_top, right_top, right_bottom, left_bottom, bottom_center    }    public enum MenuStats {        open, close;    }    int measuredHeight, measuredWidth;    public PlanetViewGroup(Context context) {        this(context, null);    }    public PlanetViewGroup(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public PlanetViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs);    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(),attrs);    }    private void init(Context context, AttributeSet attrs) {        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PlanetViewGroup);        int indexCount = typedArray.getIndexCount();        for (int i = 0; i < indexCount; i++) {            int index = typedArray.getIndex(i);            switch (index) {                case R.styleable.PlanetViewGroup_radius:                    radius = typedArray.getDimensionPixelSize(index, 50);                    break;                case R.styleable.PlanetViewGroup_MenuStats:                    int anInt = typedArray.getInt(index, 0);                    switch (anInt) {                        case 0:                            menuStats = MenuStats.open;                            break;                        case 1:                            menuStats = MenuStats.close;                            break;                    }                    break;                case R.styleable.PlanetViewGroup_CustomLayout:                    anInt = typedArray.getInt(index, 0);                    switch (anInt) {                        case 0:                            mlayout = CustomLayout.left_top;                            break;                        case 1:                            mlayout = CustomLayout.right_top;                            break;                        case 2:                            mlayout = CustomLayout.right_bottom;                            break;                        case 3:                            mlayout = CustomLayout.left_bottom;                            break;                        case 4:                            mlayout = CustomLayout.bottom_center;                            break;                    }                    break;            }        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        count = getChildCount();        for (int i = 0; i < count; i++) {            View child = getChildAt(i);            measureChild(child, widthMeasureSpec, heightMeasureSpec);        }        measuredHeight = getMeasuredHeight();        measuredWidth = getMeasuredWidth();        angle = Math.PI / (2 * (count - 2));        radius = Math.min(radius, measuredWidth / 2);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        if (!changed) return;        if (count < 3) return;//里面控件太少,菜单显示没有意思        //最后一个view作为主控件view        centerView = getChildAt(count - 1);        controllerView();        subViewLayout(false);    }    /**     * 计算子view坐标     *     * @param showAnim     */    private void subViewLayout(boolean showAnim) {        View child;        for (int i = 0; i < count - 1; i++) {            child = getChildAt(i);            if (!showAnim) {                if (menuStats == MenuStats.close) {                    child.setVisibility(GONE);                } else {                    child.setVisibility(VISIBLE);                }            }            final int index = i + 1;            if (lis != null) {                child.setOnClickListener(new OnClickListener() {                    @Override                    public void onClick(View v) {                        if (lis != null) {                            lis.menuItem(index);                        }                        menuStats = MenuStats.close;                        viewVisibleStats(GONE);                        alphaAndScale(getChildAt(index - 1));                    }                });            }            childWidth = child.getMeasuredWidth();            childHeight = child.getMeasuredHeight();            if (mlayout == CustomLayout.left_top) {                cl = (int) (radius * Math.sin((i) * angle));                ct = (int) (radius * Math.cos((i) * angle));            } else if (mlayout == CustomLayout.right_top) {                cl = measuredWidth - childWidth - (int) (radius * Math.sin((i) * angle));                ct = (int) (radius * Math.cos((i) * angle));            } else if (mlayout == CustomLayout.right_bottom) {                cl = measuredWidth - childWidth - (int) (radius * Math.sin((i) * angle));                ct = measuredHeight - childHeight - (int) (radius * Math.cos((i) * angle));            } else if (mlayout == CustomLayout.left_bottom) {                cl = (int) (radius * Math.sin((i) * angle));                ct = measuredHeight - childHeight - (int) (radius * Math.cos((i) * angle));            } else if (mlayout == CustomLayout.bottom_center) {                cl = (measuredWidth - childWidth) / 2 + (int) (radius * Math.cos((i) * (Math.PI / (count - 2))));                ct = measuredHeight - childHeight - (int) (radius * Math.sin((i) * (Math.PI / (count - 2))));            }            if (showAnim) {                if (menuStats == MenuStats.close) {                    hidePlanetMenu(child, index);                } else {                    showPlanetMenu(child, index);                }            }            cr = cl + childWidth;            cb = ct + childHeight;            child.layout(cl, ct, cr, cb);        }    }    /**     * 对view缩放和透明度变化     *     * @param child     */    private void alphaAndScale(final View child) {        child.setVisibility(VISIBLE);        ObjectAnimator alpha = ObjectAnimator.ofFloat(child, "alpha", 1, 0);        ObjectAnimator scaleX = ObjectAnimator.ofFloat(child, "scaleX", 1, 2);        ObjectAnimator scaleY = ObjectAnimator.ofFloat(child, "scaleY", 1, 2);        AnimatorSet set = new AnimatorSet();        set.setDuration(500);        set.play(alpha).with(scaleX).with(scaleY);        set.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                child.setScaleX(1);                child.setScaleY(1);            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        set.start();    }    /***     * 子菜单动画     */    private void showPlanetMenu(View child, int index) {        ObjectAnimator translationY = ObjectAnimator.ofFloat(child, "y", centerView.getTop(), ct);        ObjectAnimator translationX = ObjectAnimator.ofFloat(child, "x", centerView.getLeft(), cl);        ObjectAnimator rotation = ObjectAnimator.ofFloat(child, "rotation", 0, 360);        translationY.setInterpolator(new OvershootInterpolator());        translationX.setInterpolator(new OvershootInterpolator());        AnimatorSet set = new AnimatorSet();        set.playTogether(translationY, translationX, rotation);        set.setDuration(300 + index * 100);        set.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {                viewVisibleStats(VISIBLE);            }            @Override            public void onAnimationEnd(Animator animation) {            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        set.start();    }    /***     * 子菜单动画     */    private void hidePlanetMenu(View child, int index) {        ObjectAnimator translationY = ObjectAnimator.ofFloat(child, "y", ct, centerView.getTop());        ObjectAnimator translationX = ObjectAnimator.ofFloat(child, "x", cl, centerView.getLeft());        ObjectAnimator rotation = ObjectAnimator.ofFloat(child, "rotation", 0, 360);        AnimatorSet set = new AnimatorSet();        set.setDuration(300 + index * 100);        set.setInterpolator(new OvershootInterpolator());        set.playTogether(translationY, translationX, rotation);        set.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                viewVisibleStats(GONE);            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        set.start();    }    /**     * 主菜单控制按钮     */    private void controllerView() {        MarginLayoutParams marginLayoutParams = null;        LayoutParams layoutParams = centerView.getLayoutParams();        if(layoutParams instanceof MarginLayoutParams){            marginLayoutParams = (MarginLayoutParams)layoutParams;        }        childWidth = centerView.getMeasuredWidth();        childHeight = centerView.getMeasuredHeight();        if (mlayout == CustomLayout.left_top) {            cl = 0;            ct = 0;            cr = childWidth;            cb = childHeight;        } else if (mlayout == CustomLayout.right_top) {            cl = measuredWidth - childWidth;            ct = 0;            cr = measuredWidth;            cb = childHeight;        } else if (mlayout == CustomLayout.right_bottom) {            cl = measuredWidth - childWidth;            ct = measuredHeight - childHeight;            cr = measuredWidth;            cb = measuredHeight;        } else if (mlayout == CustomLayout.left_bottom) {            cl = 0;            ct = measuredHeight - childHeight;            cr = childWidth;            cb = measuredHeight;        } else if (mlayout == CustomLayout.bottom_center) {            cl = (measuredWidth - childWidth) / 2;            ct = measuredHeight - childHeight;            cr = cl + childWidth;            cb = measuredHeight;        }        if(marginLayoutParams == null){            centerView.layout(cl, ct , cr , cb );        }else{            centerView.layout(cl + marginLayoutParams.leftMargin, ct + marginLayoutParams.topMargin, cr + marginLayoutParams.rightMargin, cb + marginLayoutParams.bottomMargin);        }        centerView.setOnClickListener(this);    }    @Override    public void onClick(View v) {        ObjectAnimator rotation = ObjectAnimator.ofFloat(v, "rotation", 0, 360).setDuration(500);        rotation.setDuration(500);        rotation.start();        if (menuStats == MenuStats.open) {            menuStats = MenuStats.close;            subViewLayout(true);        } else {            menuStats = MenuStats.open;            subViewLayout(true);        }    }    /**     * 子view是否可见     *     * @param visible     */    private void viewVisibleStats(int visible) {        for (int i = 0; i < count - 1; i++) {            getChildAt(i).setVisibility(visible);            getChildAt(i).setAlpha(1);        }    }    public void setMenuOnClickListener(MenuOnClickListener lis) {        this.lis = lis;    }    public interface MenuOnClickListener {        void menuItem(int pos);    }}

全部实现代码就一个类,而且也不多,说明还是简单呀。坐标计算采用的是三角函数,计算子view的x,y。动画用的属性动画,大伙儿都知道属性动画最好用,因为它可以直接修改view的属性,影响最深远的就是他的点击事件,会随着view的位置而改变。这里MarginLayoutParams进行了处理,之view要使用MarginLayoutParams,就必须在父控件里面重写generateLayoutParams

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
不然会报错。。

 ((PlanetViewGroup) findViewById(R.id.planet_bottem)).setMenuOnClickListener(new PlanetViewGroup.MenuOnClickListener() {            @Override            public void menuItem(int pos) {                Toast.makeText(getApplication(), "click" + pos, Toast.LENGTH_SHORT).show();            }        });

actvivity粘贴上面这段,ok,就完了,是不是很简单呀,粘贴复制就能出效果,如果跑不起来可以来打我。。。。