Android中怎么自定義組件衛(wèi)星菜單

Android中怎么自定義組件衛(wèi)星菜單,針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

站在用戶的角度思考問題,與客戶深入溝通,找到化德網(wǎng)站設(shè)計與化德網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站建設(shè)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名與空間、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋化德地區(qū)。

首先如果要想自定義組件

1.那么第一件事就是賦予自定義組件的屬性,從效果圖上看出,該組件可以存在屏幕的各個角落點,那么位置是其屬性之一。

2.既然是衛(wèi)星菜單,那么主按鈕和其附屬的小按鈕之間的圍繞半徑也應(yīng)該作為其參數(shù)之一。

3.右圖得出,該組件包含很多按鈕,主按鈕和附屬按鈕,那么這個組件應(yīng)該繼承 ViewGroup。

一、定義衛(wèi)星菜單的屬性在 values 包下建立 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>

  <!-- 尺寸屬性dp如果使用px可能會造成屏幕適配問題-->
  <attr name="radius" format="dimension" />
  <!-- 自定義屬性-->
  <declare-styleable name="ArcMenu">

    <attr name="position" />
    <attr name="radius" />


  </declare-styleable>


</resources>

二、編寫自定義組件

package com.lanou.dllo.arcmenudemo.arcmenu;

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 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 com.lanou.dllo.arcmenudemo.R;

/**
 * Created by dllo on 16/3/25.
 * 1.首先ArcMenu是繼承ViewGroup,那么一個衛(wèi)星菜單包括一個大按鈕和其他的子按鈕群.
 */

public class ArcMenu extends ViewGroup implements View.OnClickListener {

  //設(shè)置常量,標識成枚舉
  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;

  //以下5個成員變量是所需要的.
  //聲明兩個屬性 位置 還有半徑
  private Position mPosition = Position.RIGHT_BOTTOM;
  private int mRadius;

  /**
   * 菜單的狀態(tài)
   */
  private Status mCurrentStatus = Status.CLOSE;

  /**
   * 菜單的主按鈕
   */
  private View mCButton;

  //子菜單的回調(diào)按鈕
  private OnMenuItemClickListener mMenuItemClickListener;


  /**
   * 菜單的位置枚舉類,4個位置
   */
  public enum Position {
    LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM
  }

  public enum Status {
    OPEN, CLOSE
  }

  /**
   * 點擊子菜單項,順便把位置傳遞過去
   */
  public interface OnMenuItemClickListener {
    void onClick(View view, int pos);
  }

  //3個構(gòu)造方法,相互傳遞.
  //注意別寫錯誤.
  public ArcMenu(Context context) {
    this(context, null);
  }

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

  public ArcMenu(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //TypedValue.applyDimension是轉(zhuǎn)變標準尺寸的方法 參數(shù)一:單位  參數(shù)二:默認值 參數(shù)三:可以獲取當(dāng)前屏幕的分辨率信息.
    mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
        , 100, getResources().getDisplayMetrics());

    //獲取自定義屬性的值
    //參數(shù)1:attrs AttributeSet是節(jié)點的屬性集合
    //參數(shù)2:attrs的一個數(shù)組集
    //參數(shù)3:指向當(dāng)前theme 某個item 描述的style 該style指定了一些默認值為這個TypedArray
    //參數(shù)4;當(dāng)defStyleAttr 找不到或者為0, 可以直接指定某個style
    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,
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
            , 100, getResources().getDisplayMetrics()));

    Log.d("TAG", "Position = " + mPosition + ", radius" + mRadius);
    //使用完必須回收.
    a.recycle();

  }

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

  /**
   * 測量方法
   */
  @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);
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (changed) {
      layoutCButton();
      //獲得容器內(nèi)組件的個數(shù),并且包括這個主的組件(大按鈕)
      int count = getChildCount();
      for (int i = 0; i < count - 1; i++) {
        //這里直接獲取第一個,是因為getChildAt(0)是紅色的按鈕.
        View child = getChildAt(i + 1);
        //正常來說,如果設(shè)置按鈕動畫,移動出去后,是不能點擊的,這里給按鈕設(shè)置一個隱藏的屬性.等衛(wèi)星菜單飛過去,在讓它們顯示出來.
        child.setVisibility(View.GONE);
        /**
         * 根據(jù)畫圖分析,得出每個子衛(wèi)星按鈕的夾角 a = 90°/(菜單的個數(shù)-1)
         * 假設(shè)menu總數(shù)為4,那么從左側(cè)數(shù)menu1的坐標為(0,R);
         * menu2的坐標為(R*sin(a),R*cos(a));
         * menu3的坐標為(R*sin(2a),R*cos(2a));
         * ...
         * menuN的坐標為(R,0);
         * 另:PI為π
         * */
        //測量每個子衛(wèi)星組件的在屏幕上面的坐標距離
        //這里count-2,是因為count包含了主按鈕
        //每個組件的坐標為(cl,ct);
        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();

        //如果衛(wèi)星菜單存在于底部,那么坐標位置的計算方法,就完全相反.
        /**
         * 如果菜單位置在底部 左下 ,右下.坐標會發(fā)生變化
         * */
        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() {
    // 給主按鈕設(shè)置監(jiān)聽
    mCButton = getChildAt(0);
    mCButton.setOnClickListener(this);

    //分別代表控件所處離左側(cè)和上側(cè)得距離
    int l = 0;
    int t = 0;
    int width = mCButton.getMeasuredWidth();
    int height = mCButton.getMeasuredHeight();

    /**
     * getMeasuredHeight()如果前面沒有對象調(diào)用,那么這個控件繼承ViewGroup,就意味著這是獲取容器的總高度.
     * getMeasuredWidth()也是同理.
     * 那么就可以判斷出控件在四個位置(根據(jù)坐標系判斷.)
     * */
    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;
    }

    //layout的四個屬性.分別代表主按鈕在不同位置距離屏幕左側(cè)和上側(cè)
    mCButton.layout(l, t, l + width, t + height);

  }


  @Override
  public void onClick(View v) {
    //主要確定mCButton的值
    mCButton = findViewById(R.id.id_button);
    if (mCButton == null) {
      mCButton = getChildAt(0);
    }

    //旋轉(zhuǎn)動畫
    rotateCButton(v, 0f, 360f, 300);
    //判斷菜單是否關(guān)閉,如果菜單關(guān)閉需要給菜單展開,如果菜單是展開的需要給菜單關(guān)閉.
    toggleMenu(500);
  }

  /**
   * 切換菜單
   * 參數(shù):切換菜單的時間是可控的.
   */
  public void toggleMenu(int duration) {
    //為所有子菜單添加動畫. :平移動畫丶旋轉(zhuǎn)動畫
    int count = getChildCount();
    for (int i = 0; i < count - 1; i++) {
      /**
       * 默認位置左上的話,子菜單起始坐標點為(-cl,-ct);
       *   位置右上的話,子菜單起始坐標點為(+cl,-ct);
       *   位置左下的話,子菜單起始坐標點為(-cl,+ct);
       *   位置右下的話,子菜單起始坐標點為(+cl,+ct);**
       * */
      final View childView = getChildAt(i + 1);
      //不管按鈕是開還是關(guān),子菜單必須顯示才能出現(xiàn)動畫效果.
      childView.setVisibility(View.VISIBLE);
      //平移 結(jié)束為止 0,0(以子菜單按鈕當(dāng)前位置,為坐標系.)
      int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
      int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));

      //創(chuàng)建兩個判斷變量,判別起始位置.
      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 animset = new AnimationSet(true);
      Animation tranAnim = null;

      //to open 打開的情況下
      if (mCurrentStatus == Status.CLOSE) {
        tranAnim = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);
        //當(dāng)衛(wèi)星菜單打開的時候,按鈕就可以進行點擊.
        childView.setClickable(true);
        childView.setFocusable(true);

      } else {//to close
        tranAnim = new TranslateAnimation(0, xflag * cl, 0, yflag * ct);
        //當(dāng)衛(wèi)星菜單關(guān)閉的時候,按鈕也不能隨之點擊.
        childView.setClickable(false);
        childView.setFocusable(false);
      }
      tranAnim.setFillAfter(true);
      tranAnim.setDuration(duration);
      //設(shè)置彈出速度.
      tranAnim.setStartOffset((i * 100) / count);
      //為動畫設(shè)置監(jiān)聽 如果需要關(guān)閉的話,在動畫結(jié)束的同時,需要將子菜單的按鈕全部隱藏.
      tranAnim.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        //在動畫結(jié)束時,進行設(shè)置.
        @Override
        public void onAnimationEnd(Animation animation) {
          if (mCurrentStatus == Status.CLOSE) {
//            Log.d("動畫結(jié)束狀態(tài)",mCurrentStatus +"");
            childView.setVisibility(View.GONE);
          }
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
      });

      //設(shè)置旋轉(zhuǎn)動畫(轉(zhuǎn)兩圈)
      RotateAnimation rotateAnim = new RotateAnimation(0, 720,
          Animation.RELATIVE_TO_SELF, 0.5f,
          Animation.RELATIVE_TO_SELF, 0.5f);
      rotateAnim.setDuration(duration);
      rotateAnim.setFillAfter(true);

      //把兩個動畫放到動畫集里面
      //注意動畫順序.先增加旋轉(zhuǎn)/在增加移動./
      animset.addAnimation(rotateAnim);
      animset.addAnimation(tranAnim);
      childView.startAnimation(animset);

      final int pos = i + 1;
      //設(shè)置子菜單的點擊事件
      childView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          if (mMenuItemClickListener != null) {
            mMenuItemClickListener.onClick(childView, pos);
          }
            menuItemAnim(pos - 1);
            //切換菜單狀態(tài)
            changeStatus();
        }
      });
    }
    /**
     * 當(dāng)所有子菜單切換完成后,那么菜單的狀態(tài)也發(fā)生了改變.
     * 所以changeStatus()必須放在循環(huán)外,
     * */
    //切換菜單狀態(tài)
    changeStatus();

  }


  /**
   * 切換菜單狀態(tài)
   */
  private void changeStatus() {
    //在執(zhí)行一個操作之后,如果按鈕是打開的在次點擊就會切換狀態(tài).
    mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN :
        Status.CLOSE);
    Log.d("動畫結(jié)束狀態(tài)", mCurrentStatus + "");
  }

  public boolean isOpen(){
    return mCurrentStatus ==Status.OPEN;
  }




  //設(shè)置旋轉(zhuǎn)動畫繞自身旋轉(zhuǎn)一圈 然后持續(xù)時間為300
  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);
    //保持動畫旋轉(zhuǎn)后的狀態(tài).
    anim.setFillAfter(true);
    v.startAnimation(anim);
  }

  /**
   * 添加menuItem的點擊動畫
   * */
  private void menuItemAnim(int pos) {
    for (int i = 0; i < getChildCount() - 1; i++) {
      View childView = getChildAt(i + 1);
      //在判斷條件下,寫入動畫
      //當(dāng)其中一個子菜單被點擊后,自身變大并且消失
      //其他子菜單則變小消失.
      if (i == pos) {
        childView.startAnimation(scaleBigAnim(300));
      } else {
        childView.startAnimation(scaleSmallAnim(300));
      }

      //當(dāng)子菜單被點擊之后,其他子菜單就要變成不可被點擊和獲得焦點的狀態(tài),
      childView.setClickable(false);
      childView.setFocusable(false);
    }
  }

  /**
   * 為當(dāng)前點擊的Item設(shè)置變大和透明度降低的動畫
   *
   * @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 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;
  }

}

以上就是 衛(wèi)星菜單的編寫,上面的注釋量比較大。

這里需要注意的一點。衛(wèi)星菜單在屏幕不同位置,他的動畫平移值是不一樣的。

如果實在不理解可以畫圖試試。

三、使用時注意賦予命名空間

<?xml version="1.0" encoding="utf-8"?>
<com.lanou.dllo.arcmenudemo.arcmenu.ArcMenu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:arcmenu="http://schemas.android.com/apk/res/com.lanou.dllo.arcmenudemo"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/id_menu"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  arcmenu:position="left_top"
  arcmenu:radius="140dp"
>


    <!-- 主按鈕-->
    <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:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_camera"
      android:tag="Camera"/>

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_music"
      android:tag="Music"/>

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_place"
      android:tag="Place"/>

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_sleep"
      android:tag="Sleep"/>

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_thought"
      android:tag="Sun"/>

    <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/composer_with"
      android:tag="People"/>

</com.lanou.dllo.arcmenudemo.arcmenu.ArcMenu>

關(guān)于Android中怎么自定義組件衛(wèi)星菜單問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。

名稱欄目:Android中怎么自定義組件衛(wèi)星菜單
網(wǎng)站路徑:http://muchs.cn/article12/pdpcdc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、用戶體驗、網(wǎng)頁設(shè)計公司、云服務(wù)器、全網(wǎng)營銷推廣、移動網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)