Android圓形旋轉(zhuǎn)菜單開發(fā)實(shí)例
最近幫朋友做了一個(gè)動(dòng)畫菜單,感覺有一定的實(shí)用價(jià)值,就在此給大家分享一下,先看看效果:
實(shí)現(xiàn)思路:
從圖中可以看出,這三個(gè)(或更多,需要自己再實(shí)現(xiàn))菜單是圍繞著中心點(diǎn)旋轉(zhuǎn)的,旋轉(zhuǎn)分為2層,背景旋轉(zhuǎn)和菜單旋轉(zhuǎn),背景旋轉(zhuǎn)可以直接用旋轉(zhuǎn)動(dòng)畫來實(shí)現(xiàn);菜單的旋轉(zhuǎn)是在以中心點(diǎn)為圓心的圓環(huán)上,所以這里用了根據(jù)旋轉(zhuǎn)角度求此點(diǎn)在直角坐標(biāo)系中的坐標(biāo)點(diǎn)的函數(shù)(x = r * cos(rotation* 3.14 / 180) 和y = r * sin(rotation* 3.14 / 180) ),然后根據(jù)獲取到的點(diǎn)的位置來設(shè)置菜單的位置就能實(shí)現(xiàn)這種效果。由此可見 數(shù)學(xué)是很重要的 哈哈~~
有了思路我們就能用代碼來實(shí)現(xiàn)了:
1、首先自定義View繼承相對(duì)布局并重寫構(gòu)造函數(shù)
/** * Created by ywl on 2016/8/7. */ public class CircleMenuLayout extends RelativeLayout { public CircleMenuLayout(Context context) { this(context, null); } public CircleMenuLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 初始化布局 把旋轉(zhuǎn)背景和中心點(diǎn)添加進(jìn)去 * @param context * @param attrs * @param defStyleAttr */ public CircleMenuLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; layoutInflater = LayoutInflater.from(context); menuitems = new ArrayList<View>(); centerview = new View(context);//中心點(diǎn) centerview.setId(ID_CENTER_VIEW); LayoutParams lp = new LayoutParams(0, 0); lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); addView(centerview, lp); //添加中心的 用于旋轉(zhuǎn)定位 progressBar = new ProgressBar(context);//旋轉(zhuǎn)的背景 LayoutParams lp2 = new LayoutParams(dip2px(context, 90), dip2px(context, 90)); lp2.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); addView(progressBar, lp2); progressBar.setIndeterminateDrawable(context.getResources().getDrawable(R.mipmap.icon_circle_menu)); } }
構(gòu)造函數(shù)中添加中心定位點(diǎn)和旋轉(zhuǎn)背景圖片,并設(shè)置合適的大小。
2、根據(jù)傳入的圖片數(shù)組和菜單名字?jǐn)?shù)組,生成菜單原始位置效果。
/** * 菜單的數(shù)量 和 半徑 名字 和圖片 這里只為3個(gè)菜單做了適配 * @param size * @param center_distance */ public void initMenuItem(int size, int center_distance, String[] titles, int[] imgs) { radus = 360f / size; int width = dip2px(context, 50); //菜單寬度 int height = dip2px(context, 50);//菜單高度 for(int i = 0; i < size; i++) //循環(huán)添加布局 { int top = 0; int left = 0; top = -(int)(Math.sin(radus * i * 3.1415f / 180) * center_distance); //r * cos(ao * 3.14 /180 ) left = -(int)(Math.cos(radus * i * 3.1415f / 180) * center_distance); //計(jì)算位置點(diǎn) LayoutParams lp = new LayoutParams(dip2px(context, 50), dip2px(context, 50)); View view = layoutInflater.inflate(R.layout.item_circle_menu, this, false); view.setTag(i); TextView tvname = (TextView) view.findViewById(R.id.tv_name); ImageView ivimg = (ImageView) view.findViewById(R.id.img); tvname.setText(titles[i]); ivimg.setImageResource(imgs[i]); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) {//根據(jù)點(diǎn)擊的區(qū)域 旋轉(zhuǎn)菜單 if(!isrun) { tag = (int) v.getTag(); currentPosition = tag; if(tag == 0) { finishdus = -360; } else if(tag == 1) { finishdus = -120; } else if(tag == 2) { finishdus = -240; } LayoutParams lp = (LayoutParams) v.getLayoutParams(); int l = lp.leftMargin; int t = lp.topMargin; if (t > -dip2px(context, 5) && l > -dip2px(context, 5)) { oldradus = 120f; isright = false; } else if (t > -dip2px(context, 5) && l < -dip2px(context, 5)) { oldradus = 120f; isright = true; } else if (t < -dip2px(context, 5)) { oldradus = 0f; } sub = 0; circleMenu(8, dip2px(context, 45), oldradus, isright); } } }); lp.addRule(RelativeLayout.BELOW, centerview.getId()); lp.addRule(RelativeLayout.RIGHT_OF, centerview.getId()); lp.setMargins(-width / 2 + top, -height / 2 + left, 0, 0); addView(view, lp); menuitems.add(view); } handler.postDelayed(runnable, 0); }
根據(jù)菜單的數(shù)量循環(huán)計(jì)算每個(gè)菜單的位置,然后在相應(yīng)的位置添加相應(yīng)的菜單就可以實(shí)現(xiàn)菜單的初始化了。這里為每個(gè)菜單添加了點(diǎn)擊事件,但是只適配了3個(gè)菜單的情況,至于其他數(shù)量的菜單,可以自己來改或者寫一個(gè)通用的方法來計(jì)算點(diǎn)擊位置。
3、背景旋轉(zhuǎn)動(dòng)畫:
/** * 根據(jù)度數(shù)來旋轉(zhuǎn)菜單 菜單中心都在一個(gè)圓上面 采用圓周運(yùn)動(dòng)來旋轉(zhuǎn) * @param offserradius * @param center_distance * @param d * @param right */ public void circleMenu(float offserradius, int center_distance, float d, boolean right) { if(oldradus != 0) { progressBar.clearAnimation(); if(isright) { mRotateUpAnim = new RotateAnimation(bgdus, bgdus + 120, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); bgdus += 120; } else { mRotateUpAnim = new RotateAnimation(bgdus, bgdus - 120, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); bgdus -= 120; } lir = new LinearInterpolator(); mRotateUpAnim.setDuration(350); mRotateUpAnim.setFillAfter(true); mRotateUpAnim.setInterpolator(lir); // mRotateUpAnim.setRepeatCount(Animation.INFINITE); progressBar.startAnimation(mRotateUpAnim); } circleMenuItem(offserradius, center_distance, d, right); }
這個(gè)比較簡(jiǎn)單,就是根據(jù)旋轉(zhuǎn)的角度,啟用旋轉(zhuǎn)動(dòng)畫。
4、旋轉(zhuǎn)菜單:
/** * 菜單旋轉(zhuǎn) * @param offserradius * @param center_distance * @param d * @param right */ public void circleMenuItem(float offserradius, int center_distance, float d, boolean right) { sub += offserradius; if(sub > d) { if(onMenuItemSelectedListener != null) { onMenuItemSelectedListener.onMenuItemOnclick(tag); } isrun = false; return; } if(right) { offsetradus -= offserradius; } else { offsetradus += offserradius; } int size = menuitems.size(); int width = dip2px(context, 50); int height = dip2px(context, 50); for(int i = 0; i < size; i++) { if(Math.abs(sub - d) <= 8) { offsetradus = finishdus; } LayoutParams lp = (LayoutParams) menuitems.get(i).getLayoutParams(); float ds = radus * i + offsetradus; int top = -(int)(Math.sin(ds * 3.1415f / 180) * center_distance); //r * cos(ao * 3.14 /180 ) int left = -(int)(Math.cos(ds * 3.1415f / 180) * center_distance); lp.setMargins(-width / 2 + top, -height / 2 + left, 0, 0); menuitems.get(i).requestLayout(); } if(sub <= d) { isrun = true; offsetradus = offsetradus % 360; handler.postDelayed(runnable, 5); } else { if(onMenuItemSelectedListener != null) { onMenuItemSelectedListener.onMenuItemOnclick(tag); } isrun = false; } }
這里旋轉(zhuǎn)是根據(jù)初始化時(shí)每個(gè)菜單所在的位置來求的旋轉(zhuǎn)角度,然后啟動(dòng)handler來動(dòng)遞加或遞減角度來求響應(yīng)的位置,就實(shí)現(xiàn)了動(dòng)畫效果。
5、手動(dòng)設(shè)置菜單項(xiàng)(有局限,沒有通用性):
/** * 設(shè)置旋轉(zhuǎn)到哪個(gè)菜單項(xiàng) * @param tag */ public void setCurrentTag(int tag) { if(currentPosition == tag) { return; } if(tag == 0) { finishdus = -360; } else if(tag == 1) { finishdus = -120; } else if(tag == 2) { finishdus = -240; } if(currentPosition == 0) //當(dāng)前是0 { if(tag == 1) { oldradus = 120f; isright = true; } else if(tag == 2) { oldradus = 120f; isright = false; } } else if(currentPosition == 1) { if(tag == 2) { oldradus = 120f; isright = true; } else if(tag == 0) { oldradus = 120f; isright = false; } } else if(currentPosition == 2) { if(tag == 0) { oldradus = 120f; isright = true; } else if(tag == 1) { oldradus = 120f; isright = false; } } currentPosition = tag; this.tag = tag; sub = 0; circleMenu(8, dip2px(context, 45), oldradus, isright); }
這樣就可以實(shí)現(xiàn)旋轉(zhuǎn)效果了。
6、調(diào)用方法:
(1)布局文件:
<com.ywl5320.circlemenu.CircleMenuLayout android:id="@+id/cml_menu" android:layout_width="150dp" android:layout_height="150dp" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:layout_marginBottom="92dp"/>
(2)菜單布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="100dp" android:layout_height="100dp" android:padding="5dp" android:gravity="center"> <ImageView android:id="@+id/img" android:layout_width="25dp" android:layout_height="25dp" android:scaleType="fitXY"/> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="菜單項(xiàng)" android:textSize="9sp" android:gravity="center" android:textColor="#ffffff"/> </LinearLayout>
(3)Activity中調(diào)用
<span style="white-space:pre"> </span>cmlmenu = (CircleMenuLayout) findViewById(R.id.cml_menu); btn = (Button) findViewById(R.id.btn); cmlmenu.initDatas(titles, imgs); cmlmenu.setOnMenuItemSelectedListener(new CircleMenuLayout.OnMenuItemSelectedListener() { @Override public void onMenuItemOnclick(int code) { if(code == 0)// { Toast.makeText(MainActivity.this, "支付寶", Toast.LENGTH_SHORT).show(); } else if(code == 1) { Toast.makeText(MainActivity.this, "銀聯(lián)", Toast.LENGTH_SHORT).show(); } else if(code == 2) { Toast.makeText(MainActivity.this, "微信", Toast.LENGTH_SHORT).show(); } } });
OK,就完成了三個(gè)菜單旋轉(zhuǎn)效果(注:這里僅僅是為了3個(gè)菜單而設(shè)計(jì)的,其他個(gè)數(shù)的自己還需要精簡(jiǎn)或更改一些代碼,相信自己改出來的會(huì)更有收獲的~~)。
以上所述是小編給大家介紹的Android圓形旋轉(zhuǎn)菜單開發(fā)實(shí)例,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android 自定義組件衛(wèi)星菜單的實(shí)現(xiàn)
- Android衛(wèi)星菜單效果的實(shí)現(xiàn)方法
- Android自定義VIew實(shí)現(xiàn)衛(wèi)星菜單效果淺析
- Android實(shí)現(xiàn)自定義的衛(wèi)星式菜單(弧形菜單)詳解
- Android編程實(shí)現(xiàn)仿優(yōu)酷圓盤旋轉(zhuǎn)菜單效果的方法詳解【附demo源碼下載】
- Android學(xué)習(xí)教程之圓形Menu菜單制作方法(1)
- Android自定義view實(shí)現(xiàn)圓形與半圓形菜單
- Android自定義ViewGroup實(shí)現(xiàn)帶箭頭的圓角矩形菜單
- Android仿優(yōu)酷圓形菜單學(xué)習(xí)筆記分享
- Adapter模式實(shí)戰(zhàn)之重構(gòu)鴻洋集團(tuán)的Android圓形菜單建行
- Android實(shí)現(xiàn)衛(wèi)星菜單效果
相關(guān)文章
幾個(gè)Android編程時(shí)需要注意的 web 問題
這篇文章主要介紹了幾個(gè)Android編程時(shí)需要注意的 web 問題,需要的朋友可以參考下2014-12-12Android 監(jiān)聽屏幕是否鎖屏的實(shí)例代碼
今天小編通過本文給大家分享android如何監(jiān)聽手機(jī)屏幕是否鎖屏。實(shí)現(xiàn)方法很簡(jiǎn)單,需要的朋友參考下吧2017-09-09解決android studio 打開java文件 內(nèi)容全變了的問題
這篇文章主要介紹了解決android studio 打開java文件 內(nèi)容全變了的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03利用adt-bundle輕松搭建Android開發(fā)環(huán)境與Hello world(Linux)
這篇文章主要介紹了利用adt-bundle在Linux下輕松搭建Android開發(fā)環(huán)境與Hello world,感興趣的小伙伴們可以參考一下2016-07-07Android RefreshLayout實(shí)現(xiàn)下拉刷新布局
這篇文章主要為大家詳細(xì)介紹了Android RefreshLayout實(shí)現(xiàn)下拉刷新布局,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10