中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android衛星菜單效果的實現方法

發布時間:2020-09-14 21:30:08 來源:腳本之家 閱讀:169 作者:qq_35309007 欄目:移動開發

Android小白第一次寫博客,心情無比激動。下面給大家展示一下衛星菜單的實現。

1.簡單介紹衛星菜單

在應用程序中,有很多展示菜單的方式,但其功能都是大同小異,這樣一來,菜單的美觀以及展示方式就顯的尤為重要,衛星菜單就是很不錯的一種。下面是本案例的gif圖:

Android衛星菜單效果的實現方法 

2.學習本案例需要的知識點

(1)動畫

(2)自定義ViewGroup

(3)自定義屬性

a、attr.xml

b、在布局中使用自定義屬性

c、在代碼中獲取自定義屬性值

3.首先分析我們的衛星菜單需要那些自定義屬性并書寫代碼

首先,菜單可以顯示在屏幕的四個角,所以我們需要一個屬性來確定它的位置,菜單在屏幕的四個角比較美觀,在這里用到枚舉。

其次,我們還需要一個展開半徑,因此還需要自定義半徑。

下面是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="SateMenu">
  <attr name="radius" />
  <attr name="position" />
 </declare-styleable>
</resources>

4.自定義ViewGroup

–繼承ViewGroup 以相關屬性

public class SateMenu extends ViewGroup implements View.OnClickListener {
 private int animationTime; //動畫時間
 private int radius; //展開半徑
 private int pos; //從自定義屬性中獲取的菜單位置
 private State state; //菜單狀態
 private int l = 0, t = 0; //左上值
 private View centerBtn = null; //展開按鈕
 private MenuItemListener menuItemListener; //菜單項點擊監聽
 private Position position; //枚舉型菜單位置
 private enum Position { //位置枚舉
  LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM
 }
 private enum State { //菜單狀態枚舉
  OPEN, COLSE
 }

–構造方法

public SateMenu(Context context) {
  //一個參數構造方法調用兩個參數構造方法
  this(context, null);
 }
 public SateMenu(Context context, AttributeSet attrs) {
  //兩個參數構造方法調用三個個參數構造方法
  this(context, attrs, 0);
 }
 public SateMenu(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  animationTime = 500; //設置動畫展開時間
  TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SateMenu, defStyleAttr, 0); //獲取自定義屬性值集合
  radius = (int) a.getDimension(R.styleable.SateMenu_radius,
    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics())); //獲取半徑并轉化為像素值
  state = State.COLSE; //設置菜單默認關閉
  pos = a.getInt(R.styleable.SateMenu_position, 0); //獲取位置
  //將位置轉化為枚舉值 (這樣就把無意義的int轉化為有意義的枚舉值)
  switch (pos) {
   case 0:
    position = Position.LEFT_TOP;
    break;
   case 1:
    position = Position.LEFT_BOTTOM;
    break;
   case 2:
    position = Position.RIGHT_TOP;
    break;
   case 3:
    position = Position.RIGHT_BOTTOM;
    break;
  }
 }

–重寫onMeasure方法

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

–重寫onLayout方法

 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  if (changed)
   btnLayout();
 }
 private void btnLayout() {
  centerBtn = getChildAt(0);
  if (position == Position.RIGHT_BOTTOM || position == Position.RIGHT_TOP) {
   //如果菜單設置在屏幕的右側,那么展開按鈕的l值=ViewGroup寬度-按鈕寬度
   l = getMeasuredWidth() - centerBtn.getMeasuredWidth();
  }
  if (position == Position.LEFT_BOTTOM || position == Position.RIGHT_BOTTOM) {
   //如果菜單設置在屏幕的下邊,那么展開按鈕的t值=ViewGroup高度-按鈕高度
   t = getMeasuredHeight() - centerBtn.getMeasuredHeight();
  }
  //設置展開按鈕位置
  centerBtn.layout(l, t, l + centerBtn.getMeasuredWidth(), t + centerBtn.getMeasuredHeight());
  childBtnlayout(); //設置子按鈕位置
  centerBtn.setOnClickListener(this);
 }

–設置子按鈕位置需要一點點數學知識,下面我以主菜單在右下角為例,畫一個簡圖,圖片對應右側第一個公式

Android衛星菜單效果的實現方法

 private void childBtnlayout() {
  int childMuneCount = getChildCount() - 1;
  //角度等于90度/子按鈕個數-1
  float a = (float) (Math.PI / 2 / (childMuneCount - 1));
  int cl, ct; //分別是子按鈕的 左 上 
  for (int i = 0; i < childMuneCount; i++) {
   if (position == Position.RIGHT_BOTTOM || position == Position.RIGHT_TOP) {
    cl = (int) (l - radius * Math.cos(i * a));
   } else {
    cl = (int) (l + radius * Math.cos(i * a));
   }
   if (position == Position.LEFT_TOP || position == Position.RIGHT_TOP) {
    ct = (int) (t + radius * Math.sin(i * a));
   } else {
    ct = (int) (t - radius * Math.sin(i * a));
   }
   View childView = getChildAt(i + 1);
   childView.layout(cl, ct, cl + childView.getMeasuredWidth(), ct + childView.getMeasuredHeight());
   childView.setOnClickListener(this);
   childView.setTag(i);
   childView.setVisibility(View.GONE);
  }
 }

–動畫的展開與關閉,這里沒有用屬性動畫,原理是:當用戶關閉菜單的時候,將子按鈕隱藏,打開才打的時候在把子按鈕顯示出來

 private void changeState() {
  int childMuneCount = getChildCount() - 1;
  //設置展開按鈕旋轉動畫
  Animation animation = new RotateAnimation(0, 360, centerBtn.getMeasuredWidth() / 2, centerBtn.getMeasuredHeight() / 2);
  animation.setDuration(animationTime);
  centerBtn.setAnimation(animation);
  animation.start();
  View childView;
  //子按鈕有兩個動畫(位移、旋轉),所以這里用到動畫集,這里也涉及到一些數學知識,和之前設置子按鈕位置差不多
  AnimationSet animationSet;
  Animation translateAnimation;
  Animation rotateAnimation;
  int cl, ct;
  float a = (float) (Math.PI / 2 / (childMuneCount - 1));
  if (state == State.OPEN) {
   state = State.COLSE;
   for (int i = 0; i < childMuneCount; i++) {
    if (position == Position.RIGHT_BOTTOM || position == Position.RIGHT_TOP)
     cl = (int) (radius * Math.cos(i * a));
    else
     cl = (int) (-radius * Math.cos(i * a));
    if (position == Position.LEFT_TOP || position == Position.RIGHT_TOP)
     ct = (int) (-radius * Math.sin(i * a));
    else
     ct = (int) (radius * Math.sin(i * a));
    childView = getChildAt(i + 1);
    childView.setVisibility(View.GONE);
    translateAnimation = new TranslateAnimation(0, cl, 0, ct);
    translateAnimation.setDuration(animationTime);
    rotateAnimation = new RotateAnimation(0, 360, childView.getMeasuredHeight() / 2, childView.getMeasuredHeight() / 2);
    rotateAnimation.setDuration(animationTime);
    animationSet = new AnimationSet(true);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(translateAnimation);
    childView.setAnimation(animationSet);
    animationSet.start();
    childView.setVisibility(View.GONE);
   }
  } else {
   state = State.OPEN;
   for (int i = 0; i < childMuneCount; i++) {
    if (position == Position.RIGHT_BOTTOM || position == Position.RIGHT_TOP)
     cl = (int) (radius * Math.cos(i * a));
    else
     cl = (int) (-radius * Math.cos(i * a));
    if (position == Position.LEFT_TOP || position == Position.RIGHT_TOP)
     ct = (int) (-radius * Math.sin(i * a));
    else
     ct = (int) (radius * Math.sin(i * a));
    childView = getChildAt(i + 1);
    childView.setVisibility(View.GONE);
    translateAnimation = new TranslateAnimation(cl, 0, ct, 0);
    translateAnimation.setDuration(animationTime);
    rotateAnimation = new RotateAnimation(360, 0, childView.getMeasuredHeight() / 2, childView.getMeasuredHeight() / 2);
    rotateAnimation.setDuration(animationTime);
    animationSet = new AnimationSet(true);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(translateAnimation);
    childView.setAnimation(animationSet);
    animationSet.start();
    childView.setVisibility(View.VISIBLE);
   }
  }
 }

–寫到這里我們的衛星菜單已經可以展現出來的,運行一下,效果還是不錯的。美中不足的是,子按鈕還沒有點擊事件,下面我們就將這個小小的不足補充一下。我們可以通過給子按鈕添加點擊事件來監聽它,但點擊之后要做的事情不可能寫在ViewGroup中,這就需要用接口進行回調。大家看一下在設置子按鈕位置的時候有這樣一句代碼 childView.setTag(i); 它的目的就是給子按鈕添加索引,接下來看一下具體怎樣實現的。

 @Override
 public void onClick(View v) {
  if (v.getId() == centerBtn.getId()) {
   changeState();
  } else {
   if (menuItemListener != null) {
    menuItemListener.onclick((Integer) v.getTag());
   }
  }
 }
 public interface MenuItemListener {
  void onclick(int position);
 }
 public void setMenuItemListener(MenuItemListener menuItemListener) {
  this.menuItemListener = menuItemListener;
 }

–到這里我們已經完全實現了衛星菜單的所有功能,但大家有沒有發現,一些菜單在展開之后,我們點擊其他區域,菜單會自動收起來,所以我們還要給我們的ViewGroup添加onTouchEvent事件,在菜單展開的時候,他把菜單收起來,并將此次點擊攔截。

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (state == State.OPEN) {
   changeState();
   return true; //攔截
  }
  return super.onTouchEvent(event);
 }

5.下面試用一下我們編寫的衛星菜單,看一下成果。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:wzw="http://schemas.android.com/apk/res/com.satemenudemo"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 <com.satemenudemo.SateMenu
  android:id="@+id/menu_id"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_margin="3dp"
  wzw:position="right_bottom"
  wzw:radius="150dp">
  <ImageButton
   android:id="@+id/center_btn"
   android:layout_width="40dp"
   android:layout_height="40dp"
   android:background="@drawable/add" />
  <ImageButton
   android:id="@+id/menu1"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="@drawable/find" />
  <ImageButton
   android:id="@+id/menu2"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="@drawable/shop" />
  <ImageButton
   android:id="@+id/menu3"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="@drawable/people" />
  <ImageButton
   android:id="@+id/menu4"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="@drawable/love" />
 </com.satemenudemo.SateMenu>
</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  SateMenu sateMenu = (SateMenu) findViewById(R.id.menu_id);
  sateMenu.setMenuItemListener(new SateMenu.MenuItemListener() {
   @Override
   public void onclick(int position) {
    Toast.makeText(MainActivity.this, "-- "+position, Toast.LENGTH_SHORT).show();
   }
  });
 }
}

以上所述是小編給大家介紹的Android衛星菜單效果的實現方法,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

陇川县| 资溪县| 汝州市| 印江| 孟连| 鹤峰县| 芜湖市| 玛沁县| 安义县| 达孜县| 阳原县| 大姚县| 井陉县| 兴山县| 乐山市| 清苑县| 赤城县| 法库县| 四平市| 洪泽县| 蓬莱市| 沂南县| 潢川县| 永泰县| 太仓市| 霍州市| 同心县| 中江县| 云梦县| 额尔古纳市| 京山县| 宣化县| 湘潭县| 兴隆县| 峡江县| 寿阳县| 浏阳市| 波密县| 佳木斯市| 昌宁县| 德清县|