衛星菜單

轉載請註明出處:http://blog.csdn.net/forwardyzk/article/details/44306855

下面就介紹一種菜單,衛星菜單(旋轉菜單).按照下面的步驟來介紹其實現的方法。

衛星菜單
1.自定義類繼承ViewGroup
2.自定義屬性(在res/value/attr.xml中添加屬性位置和半徑 )
3.在View中獲取這些自定義的屬性
4.onMeasure測量子孩子
5.設置菜單主按鈕的位置
6.設置子孩子的位置
7.給Item設置平移動畫和旋轉動畫
8.點擊Item設置點擊事件並且設置縮放動畫
9.測試使用

步驟1

   -自定義類(MoonMenu)繼承ViewGroup

public class MoonMenu extends ViewGroup {
添加兩個構造方法,其中一個必須含有設置屬性的構造方法

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

    public MoonMenu(Context context, AttributeSet attrs) {
        super(context, attrs);


步驟2

    --自定義屬性(在res/value/attr.xml中添加屬性位置和半徑 )

定義屬性,顯示位置,使用枚舉,有四個位置供選擇,左上,左下,右上,右下。

半徑大小,是一個旋轉的弧形,設置一個半徑的大小

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MoonMenu">
        <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>

</resources>

步驟3.

   --在View中獲取這些自定義的屬性

在調用出,要添加一個命名空間,xmlns:moon="http://schemas.android.com/apk/res-auto"

 <com.example.menu.view.MoonMenu
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        moon:position="LEFT_TOP"
        moon:radius="130dp">

    </com.example.menu.view.MoonMenu>

在構造方法中獲取設置的自定義的位置和半徑屬性

定義一個枚舉位置

enum Position {
        LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM
    }


public MoonMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        //獲取自定義的屬性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MoonMenu);

        int p = typedArray.getInt(R.styleable.MoonMenu_position, 3);
        if (p == 0) {
            position = Position.LEFT_TOP;
        } else if (p == 1) {
            position = Position.LEFT_BOTTOM;
        } else if (p == 2) {
            position = Position.RIGHT_TOP;
        } else if (p == 3) {
            position = Position.RIGHT_BOTTOM;
        }
        radius = (int) typedArray.getDimension(R.styleable.MoonMenu_radius, 100);
    }
半徑默認爲100dp,位置默認右下角


步驟4

  --測量子孩子的長和寬,供在onLayout方法中顯示

  菜單的主按鈕和菜單Item都作爲其MoonMenu菜單子孩子顯示

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

步驟5

  --設置菜單主按鈕的位置

 現在控件中添加菜單主按鈕到MoonMenu,作爲第一個子孩子

<com.example.menu.view.MoonMenu
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        moon:position="LEFT_TOP"
        moon:radius="130dp">
        <!--菜單主按鈕-->
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <ImageView
                android:layout_centerInParent="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/main" />
        </RelativeLayout>
    </com.example.menu.view.MoonMenu>


/**
     * 設置菜單的主按鈕
     */
    private void layoutMainView() {
        mainView = getChildAt(0);
        //設置菜單主按鈕的點擊事件
        mainView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                switchStaOffOn(v);
            }
        });
        int sWidth = getMeasuredWidth();
        int sHeight = getMeasuredHeight();
        int l = 0;
        int t = 0;
        int width = mainView.getMeasuredWidth();
        int height = mainView.getMeasuredHeight();
        switch (position) {
            case LEFT_TOP:
                mainView.layout(l, t, width, height);
                break;
            case LEFT_BOTTOM:
                mainView.layout(l, sHeight - height, width, sHeight);
                break;
            case RIGHT_TOP:
                mainView.layout(sWidth - width, t, sWidth, height);
                break;
            case RIGHT_BOTTOM:
                mainView.layout(sWidth - width, sHeight - height, sWidth, sHeight);
                break;
        }

    }

通過獲取菜單的長和寬,子孩子的長和寬,確定View顯示的座標,使用layout顯示的界面上。


以左上角的位置作爲例子。

l=0:childView左邊相對於父View的座標

t=0:childView頂部邊相對於父View的座標

r=childView的寬度:childView右邊相對於父View的座標

b=childView的高度:childView底部邊相對於父View的座標

 mainView.layout(l, t, r, b);


步驟6

     ---設置子孩子MenuItem的位置

先一下一張圖


MenuItem可以有半徑(R),角度(θ)確定兩個距離,這樣在獲取childView的寬和高,Menu的長和寬,就可以確定MenItem相對父View的座標

R:是設置Menu時,傳進來的。

θ:由MenuItem 的個數確定,例如有三個MenuItem,單位的角度就是:α=Math.PI / 2 / (3 - 2),爲什麼-2呢,第一去掉主按鈕,第二去掉MenuItem-1.那三個MenuItem的計算的角度分別爲:0*α,1*α,2*α


那麼設置顯示位置也是用layout(l,t,r,b)

/**
     * 設置子菜單Item位置
     */
    private void layoutChildsItem() {
        int childCount = getChildCount();
        double angleUnit = Math.PI / 2 / (childCount - 2);
        for (int i = 0; i < childCount - 1; i++) {
            View childView = getChildAt(i + 1);
            final int clickPosition = i + 1;
            //默認的是顯示
            int xl = (int) (radius * Math.sin(angleUnit * i));
            int yl = (int) (radius * Math.cos(angleUnit * i));
            int childHeight = childView.getMeasuredHeight();
            int childWidth = childView.getMeasuredWidth();
            int screenWidth = getMeasuredWidth();
            int screenHeight = getMeasuredHeight();
            switch (position) {
                case LEFT_TOP:
                    childView.layout(xl, yl, xl + childWidth, yl + childHeight);
                    break;
                case LEFT_BOTTOM:
                    childView.layout(xl, screenHeight - yl - childHeight, xl + childWidth, screenHeight - yl);
                    break;
                case RIGHT_TOP:
                    childView.layout(screenWidth - xl - childWidth, yl, screenWidth - xl, yl + childHeight);
                    break;
                case RIGHT_BOTTOM:
                    childView.layout(screenWidth - xl - childWidth, screenHeight - yl - childHeight, screenWidth - xl, screenHeight - yl);
                    break;
            }
            childView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onMenuItemClickListener != null) {
                        onMenuItemClickListener.onClickMenuItem(v, clickPosition);
                    }
                    onMenuItemClickAnimation(v);

                }
            });
        }
    }

步驟7

  ---給MainMenuItem設置點擊事件和給Item設置平移動畫和旋轉動畫

     主菜單按鈕旋轉動畫,其他的ChildItem平移動畫和旋轉動畫

     切換菜單的開關狀態


在layoutMainView()方法中,給第一個ChildItem(菜單主按鈕)設置點擊事件

mainView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                switchStaOffOn(v);
            }
        });

 /**
     * 主菜單點擊事件執行的操作
     *
     * @param mainView
     */
    private void switchStaOffOn(View mainView) {

        mainAnim(mainView, DURATION);
        childItemAnim(DURATION);
        changeStatus();

    }

/**
     * 菜單主按鈕設置旋轉動畫
     *
     * @param mainView
     */
    private void mainAnim(View mainView, int duration) {
        RotateAnimation roateAn = new RotateAnimation(0, 360, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        roateAn.setFillAfter(true);
        roateAn.setDuration(duration);
        mainView.setAnimation(roateAn);
        mainView.startAnimation(roateAn);
    }
當點擊了主按鈕時,主菜單按鈕旋轉360度。


給其他的ChildItem要設置平移動畫和旋轉動畫

其座標都是相對與父View作爲參考物

當菜單展開時:

對於LEFT_BOTTOM,LEFT_TOP位置的菜單,當平移的時候,向左移動,X座標變小(與移動前X座標比較)

對於RIGHT_TOP,LEFT_TOP位置的菜單,  當平移的時候,向上移動,Y座標變小(與移動前Y座標比較)

爲X座標和Y座標大小變化增加標記,xFlag和yFlag,默認爲1,表示變大,-1表示變小

當菜單打開的時候,X和Y座標都是(0,0)--->>(xFlag * xl,yFlag * yl)

當菜單關閉的時候,X和Y的座標是xFlag * xl,yFlag * yl)-->>0,0)--->>

給Item增加旋轉動畫

相對於自己中心位置旋轉,0-->720,旋轉兩圈


ChildMenuItem設置了動畫的開始執行的間隔時間,這樣的效果更好,感覺就是按順序展開是關閉。setStartOffset(開始延遲時間執行)

/**
     * 給Item設置展開與收回的動畫
     *
     * @param duration
     */
    private void childItemAnim(long duration) {

        int xFlag = 1;
        int yFlag = 1;
        if (position == Position.LEFT_BOTTOM || position == Position.LEFT_TOP) {
            xFlag = -1;
        }
        if (position == Position.RIGHT_TOP || position == Position.LEFT_TOP) {
            yFlag = -1;
        }
        int childCount = getChildCount();
        double angleUnit = Math.PI / 2 / (childCount - 2);
        for (int i = 0; i < childCount - 1; i++) {
            final View childView = getChildAt(i + 1);
            childView.setVisibility(View.VISIBLE);
            int xl = (int) (radius * Math.sin(angleUnit * i));
            int yl = (int) (radius * Math.cos(angleUnit * i));
            AnimationSet anSet = new AnimationSet(true);
            //平移動畫
            TranslateAnimation translateAnim = null;
            //旋轉動畫
            RotateAnimation rotateAni = null;
            if (switchStatus == Status.OPEN) {
                translateAnim = new TranslateAnimation(0, xFlag * xl, 0, yFlag * yl);
                childView.setClickable(false);
                childView.setFocusable(false);
            } else if (switchStatus == Status.CLOSE) {
                translateAnim = new TranslateAnimation(xFlag * xl, 0, yFlag * yl, 0);
                childView.setClickable(true);
                childView.setFocusable(true);
            }
            translateAnim.setFillAfter(true);
            translateAnim.setDuration(duration);
            rotateAni = new RotateAnimation(0, 720, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
            rotateAni.setFillAfter(true);
            rotateAni.setDuration(duration);

            //添加動畫到集合
            translateAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

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

                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            anSet.addAnimation(rotateAni);
            anSet.addAnimation(translateAnim);
            anSet.setStartOffset(i * 100 / childCount);
            childView.startAnimation(anSet);
        }

    }


默認所有的ChildMenuIem都顯示出來 childView.setVisibility(View.VISIBLE);
當動畫結束的時候,如果此時菜單的狀態爲關閉狀態,那麼ChildMenuItem,設置爲不可顯示,

最後改變菜單開和關狀態

/**
     * 改變開關的狀態
     */
    private void changeStatus() {
        switchStatus = (switchStatus == Status.OPEN ? Status.CLOSE : Status.OPEN);
    }



/**
     * 判斷當前的Menu是否是開的狀態
     *
     * @return
     */
    public boolean isOpen() {
        return switchStatus == Status.OPEN;
    }

    /**
     * Menu從開---關的ChildMenuItem動畫效果,並且要改變Menu的狀態
     */
    public void toClose() {
        childItemAnim(DURATION);
        changeStatus();
    }


步驟8

  ---點擊Item設置點擊事件並且設置縮放動畫

   當點擊Item的時候,點擊的ChildMenuItem擴大,其他的ChildMenuItem縮小,同時透明度變小

在layoutChildsItem()中給ChildMenuItem設置點擊事件

childView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onMenuItemClickListener != null) {
                        onMenuItemClickListener.onClickMenuItem(v, clickPosition);
                    }
                    onMenuItemClickAnimation(v);

                }
            });

/**
     * 點擊菜單Item的動畫,點擊的Item放大,其他的縮小,並且透明度變低
     *
     * @param v
     */
    private void onMenuItemClickAnimation(View v) {

        int childCount = getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View childView = getChildAt(i + 1);
            if (childView == v) {
                childView.startAnimation(menuItemBigAnimation());
            } else {
                childView.startAnimation(menuItemSmallAnimation());

            }
            childView.setClickable(false);
            childView.setFocusable(false);
            if (isOpen()) {
                changeStatus();
            }
        }
    }


 /**
     * 點擊Item後,Item變大和透明度變大的動畫
     *
     * @return
     */
    private Animation menuItemBigAnimation() {
        AlphaAnimation alphaAnima = new AlphaAnimation(1.0f, 0.0f);
        //點擊的item動畫
        ScaleAnimation scaleAnimaClick = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        AnimationSet clickSet = new AnimationSet(true);
        clickSet.addAnimation(scaleAnimaClick);
        clickSet.addAnimation(alphaAnima);
        clickSet.setFillAfter(true);
        clickSet.setDuration(300);
        return clickSet;
    }

    /**
     * 點擊Item後,Item變小和透明度變小的動畫
     *
     * @return
     */
    private Animation menuItemSmallAnimation() {
        AlphaAnimation alphaAnima = new AlphaAnimation(1.0f, 0.0f);
        alphaAnima.setDuration(300);
        ScaleAnimation scaleAnima = new ScaleAnimation(1.0f, 0.0f, 0.0f, 0.0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        scaleAnima.setDuration(300);
        AnimationSet set = new AnimationSet(true);
        set.addAnimation(scaleAnima);
        set.addAnimation(alphaAnima);
        set.setFillAfter(true);
        set.setDuration(300);
        return set;
    }

定義點擊事件的回調接口,

 /**
     * 設置點擊Item的事件監聽
     *
     * @param onMenuItemClickListener
     */
    public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
        this.onMenuItemClickListener = onMenuItemClickListener;
    }

    /**
     * 點擊Item的監聽事件
     */
    public interface OnMenuItemClickListener {
        void onClickMenuItem(View itemView, int position);
    }

    private OnMenuItemClickListener onMenuItemClickListener;

當然對於動畫效果,是寫在了當前的空間中,不需要在回調接口中使用,當點擊了ChildMenuItem後,執行的操作在回調接口中操作。

參數:點擊的當前的ChildMenuView和當前ChildMenuView位置。


步驟9

   ---測試使用


在一個Activity中,有一個菜單和ListView展示,默認的都展示出來

test_activity.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:moon="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"></ListView>

    <!--右下角-->
    <com.example.menu.view.MoonMenu
        android:id="@+id/moonmenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        moon:position="RIGHT_BOTTOM"
        moon:radius="150dp">

        <RelativeLayout
            android:id="@+id/main_menu"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

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

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_1"
            android:tag="1" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_2"
            android:tag="2" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_3"
            android:tag="3" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_4"
            android:tag="4" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_5"
            android:tag="5" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_6"
            android:tag="6" />

    </com.example.menu.view.MoonMenu>

</RelativeLayout>

TestAcitivity.java

public class TestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
        initView();
    }

    private void initView() {
        final MoonMenu moonMenu = (MoonMenu) findViewById(R.id.moonmenu);
        moonMenu.setOnMenuItemClickListener(new MoonMenu.OnMenuItemClickListener() {
            @Override
            public void onClickMenuItem(View itemView, int position) {
                //可以使用標記來判斷點擊的Viwew,當然必須給ChildViewItem設置了Tag
                if ("1".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                } else if ("2".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                } else if ("3".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                } else if ("4".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                } else if ("5".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                } else if ("6".equals(itemView.getTag())) {
                    Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                }

                //可以使用position來判斷
                switch (position) {
                    case 1:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;
                    case 2:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;
                    case 3:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;
                    case 4:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;
                    case 5:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;
                    case 6:
                        Toast.makeText(getApplicationContext(), "點擊了" + position, Toast.LENGTH_SHORT).show();
                        break;


                }


            }
        });
        //展示ListView,並且設置適配器和Item點擊事件,滾動監聽
        ListView listview = (ListView) findViewById(R.id.listview);
        String[] items = {"item0", "item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10", "item11", "item12", "item13", "item14", "item15", "item16", "item17", "item18", "item19", "item20"};
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(TestActivity.this, android.R.layout.simple_list_item_activated_1, items);
        listview.setAdapter(adapter);
        listview.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (moonMenu.isOpen()) {
                    moonMenu.toClose();
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (moonMenu.isOpen()) {
                    moonMenu.toClose();
                }
            }
        });
    }


}


當ListView滾動的時候,如果當前的Menu是開的狀態,那麼就進行關閉的動畫。

判斷點擊的ChildMenuItem,可以使用Tag(必須在xml文件中設置了Tag),或者使用position來判斷。


效果圖:



源碼下載:   http://download.csdn.net/detail/forwardyzk/8506243


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章