自定义控件:旋转菜单
效果圖
項目概述
首先,我們學習如何自定義一個組合控件,其中,優酷菜單是一個典型的自定義組合控件,它的效果圖如圖1-1 所示:
圖中由中間往外,分別是一級菜單、二級菜單、三級菜單。其基本用法是:點擊一級菜單后加載二級菜單,再點擊二級菜單加載三級菜單,如圖1-2(c)—(d)—(e)—(f),再點擊一級菜單分別隱藏三級、二級菜單
1-2(a)—(b)。并且點擊手機菜單鍵,讓菜單根據狀態來顯示和隱藏,演示效果圖如圖1-2 所示。
優酷菜單UI
優酷菜單的整體布局采用RelativeLayout,每一級菜單都是一個RelativeLayout。優酷菜單的布局文件activity_main.xml,具體的代碼如文件【1-1】所示:
【文件1-1】activity_main.xml
運行程序,效果圖如圖1-3 所示
優酷菜單業務邏輯實現
布局UI 實現之后,我們需要實現優酷菜單的業務邏輯代碼,具體代碼如文件【1-2】所示:【文件1-2】com.itheima.youku.MainActivity
package com.github.rotatemenu;import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout;/*** ============================================================* Copyright:${TODO}有限公司版權所有 (c) 2017* Author: AllenIverson* Email: 815712739@qq.com* GitHub: https://github.com/JackChen1999* 博客: http://blog.csdn.net/axi295309066* 微博: AndroidDeveloper* <p>* Project_Name:RotateMenu* Package_Name:com.github.rotatemenu* Version:1.0* time:2016/2/28 21:47* des :三級旋轉菜單* gitVersion:$Rev$* updateAuthor:$Author$* updateDate:$Date$* updateDes:${TODO}* ============================================================*/ public class MainActivity extends Activity implements View.OnClickListener {private RelativeLayout rlLevel1, rlLevel2, rlLevel3;private boolean isLevel1Show = true;private boolean isLevel2Show = true;private boolean isLevel3Show = true;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView ivHome = (ImageView) findViewById(R.id.iv_home);ImageView ivMenu = (ImageView) findViewById(R.id.iv_menu);rlLevel1 = (RelativeLayout) findViewById(R.id.rl_level1);rlLevel2 = (RelativeLayout) findViewById(R.id.rl_level2);rlLevel3 = (RelativeLayout) findViewById(R.id.rl_level3);ivHome.setOnClickListener(this);ivMenu.setOnClickListener(this);// 為了避免第三層布局將一二層事件攔截掉, 需要在布局文件中最先注冊第三層, 最后注冊第一層rlLevel3.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.iv_home:System.out.println("home clicked!");if (Tools.mAnimtionNum != 0) {break;}if (isLevel2Show) {Tools.hideView(rlLevel2);// 隱藏第二層布局isLevel2Show = false;if (isLevel3Show) {// 如果發現第三次也展現, 也需要隱藏Tools.hideView(rlLevel3, 200);// 動畫延時200 毫秒再運行isLevel3Show = false;}} else {Tools.showView(rlLevel2);isLevel2Show = true;}break;case R.id.iv_menu:if (Tools.mAnimtionNum != 0) {break;}System.out.println("menu clicked!");if (isLevel3Show) {Tools.hideView(rlLevel3);isLevel3Show = false;} else {Tools.showView(rlLevel3);isLevel3Show = true;}break;default:break;}}//監聽用戶的物理按鍵@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_MENU) {if (Tools.mAnimtionNum != 0) {return true;}if (isLevel1Show) {Tools.hideView(rlLevel1);isLevel1Show = false;if (isLevel2Show) {Tools.hideView(rlLevel2, 200);isLevel2Show = false;}if (isLevel3Show) {Tools.hideView(rlLevel3, 300);isLevel3Show = false;}} else {Tools.showView(rlLevel1);isLevel1Show = true;Tools.showView(rlLevel2, 200);isLevel2Show = true;}return true;}return super.onKeyDown(keyCode, event);} }Tools 工具類的邏輯實現
為了隱藏View 和顯示View,在工具類Tools.java 中定義了兩個方法,具體代碼如文件【】所示:【文件1-3】com.itheima.youku.Tools
package com.github.rotatemenu;import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.RotateAnimation;/*** ============================================================* Copyright:${TODO}有限公司版權所有 (c) 2017* Author: AllenIverson* Email: 815712739@qq.com* GitHub: https://github.com/JackChen1999* 博客: http://blog.csdn.net/axi295309066* 微博: AndroidDeveloper* <p>* Project_Name:RotateMenu* Package_Name:com.github.rotatemenu* Version:1.0* time:2016/2/28 21:47* des :三級旋轉菜單* gitVersion:$Rev$* updateAuthor:$Author$* updateDate:$Date$* updateDes:${TODO}* ============================================================*/public class Tools {public static void hideView(ViewGroup view) {hideView(view, 0);}public static void showView(ViewGroup view) {showView(view, 0);}public static int mAnimtionNum = 0; //用于記錄當前正在執行的動畫個數/*** 隱藏動畫** @param view 將要執行動畫的視圖* @param delay 動畫要延遲執行的時間*/public static void hideView(ViewGroup view, long delay) {/*** 第一個參數: fromDegrees 起始角度,這里我們設置為0* 第二個參數: toDegrees 目標角度,這里設置為180 度* 第三個參數: pivotXType 相對于X 坐標類型,這里是相對于自己* 第四個參數: pivotXValue 相對于X 坐標類型的值,這里是0.5f,也就是X 軸的一半* 第五個參數: pivotYType 相對于Y 坐標類型,這里是相對于自己* 第六個參數: pivotYValue 相對于Y 坐標類型的值,這里是1.f,也就是Y 坐標最大處* RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType,pivotYValue)*/RotateAnimation anim = new RotateAnimation(0, 180,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 1f);anim.setDuration(500); //動畫執行時間anim.setFillAfter(true); // 保持動畫后的狀態anim.setStartOffset(delay); // 延遲多長時間后才運行動畫anim.setAnimationListener(new MyAnimationListener());view.startAnimation(anim);// 禁用所有孩子的點擊事件int childCount = view.getChildCount();for (int i = 0; i < childCount; i++) {view.getChildAt(i).setEnabled(false); // 禁用點擊事件}}/*** 顯示動畫** @param view 將要執行動畫的視圖* @param delay 動畫要延遲執行的時間*/public static void showView(ViewGroup view, long delay) {RotateAnimation anim = new RotateAnimation(180, 360,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 1f);anim.setDuration(500);anim.setFillAfter(true); // 保持動畫后的狀態anim.setStartOffset(delay); // 延遲多長時間后才運行動畫anim.setAnimationListener(new MyAnimationListener());view.startAnimation(anim);// 開啟所有孩子的點擊事件int childCount = view.getChildCount();for (int i = 0; i < childCount; i++) {view.getChildAt(i).setEnabled(true);// 開啟點擊事件}}public static class MyAnimationListener implements Animation.AnimationListener {@Overridepublic void onAnimationStart(Animation animation) {mAnimtionNum++;}@Overridepublic void onAnimationEnd(Animation animation) {mAnimtionNum--;}@Overridepublic void onAnimationRepeat(Animation animation) {}} }知識點總結
1.補間動畫不能改變控件的實際位置,控件還是能夠響應原先的事件。在菜單隱藏后還會響應點擊事件,因此在Tools.java 的第32 到36 行在隱藏菜單時,通過遍歷相對布局的子控件,設置其為不可用來解決此bug,
在顯示菜單時,第51 到55 行通過遍歷相對布局的子控件,設置為可用。
2.連續點擊菜單時,優酷菜單動畫會直接執行,產生一個隱藏動畫還沒執行完,就執行顯示動畫的bug,因此在Tools.java 的隱藏和顯示動畫中都設置了動畫監聽MyAnimationListener,在點擊菜單時,先判斷Tools
的動畫數量mAnimtionNum(Tools.java 第8 行)是否為0,再執行下一個動畫,來解決bug。
布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><Button android:id="@id/btn_menu"android:layout_width="280dp"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_centerHorizontal="true"android:layout_marginLeft="15dp"android:layout_marginRight="15dp"android:text="Menu鍵"android:textColor="#fff"android:background="@drawable/progress_normal"android:textAllCaps="false"/><!--一級菜單--><RelativeLayout android:id="@+id/rl_level1"android:layout_width="100dp"android:layout_height="50dp"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:background="@mipmap/level1"><ImageView android:id="@+id/iv_home"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_centerVertical="true"android:src="@mipmap/icon_home"/></RelativeLayout><!--二級菜單--><RelativeLayout android:id="@+id/rl_level2"android:layout_width="180dp"android:layout_height="90dp"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:background="@mipmap/level2"><ImageView android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_margin="10dp"android:src="@mipmap/icon_search"/><ImageView android:id="@+id/iv_menu"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="5dp"android:src="@mipmap/icon_menu"/><ImageView android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_margin="10dp"android:src="@mipmap/icon_myyouku"/></RelativeLayout><!--三級菜單--><include layout="@layout/menu_level3"/></RelativeLayout> <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/rl_level3"android:layout_width="280dp"android:layout_height="140dp"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:background="@mipmap/level3"><ImageView android:id="@+id/channel1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_margin="10dp"android:src="@mipmap/channel1"/><ImageView android:id="@+id/channel2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@+id/channel1"android:layout_alignLeft="@+id/channel1"android:layout_marginLeft="25dp"android:layout_marginBottom="5dp"android:src="@mipmap/channel2"/><ImageView android:id="@+id/channel3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@+id/channel2"android:layout_alignLeft="@+id/channel2"android:layout_marginLeft="35dp"android:layout_marginBottom="5dp"android:src="@mipmap/channel3"/><ImageView android:id="@+id/channel4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="5dp"android:src="@mipmap/channel4"/><ImageView android:id="@+id/channel5"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@+id/channel6"android:layout_alignRight="@+id/channel6"android:layout_marginBottom="5dp"android:layout_marginRight="35dp"android:src="@mipmap/channel5"/><ImageView android:id="@+id/channel6"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@+id/channel7"android:layout_alignRight="@+id/channel7"android:layout_marginBottom="5dp"android:layout_marginRight="25dp"android:src="@mipmap/channel6"/><ImageView android:id="@+id/channel7"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_margin="10dp"android:src="@mipmap/channel7"/></RelativeLayout>實現代碼
RotateMenuActivity.java
public class RotateMenuActivity extends AppCompatActivity implements View.OnClickListener{@Bind(R.id.iv_home)public ImageView mIv_home;@Bind(R.id.iv_menu)public ImageView mIv_menu;@Bind(R.id.rl_level1)public RelativeLayout mLevel1;@Bind(R.id.rl_level2)public RelativeLayout mLevel2;@Bind(R.id.rl_level3)public RelativeLayout mLevel3;@Bind(R.id.btn_menu)public Button btn_menu;private boolean isShowlevel2 = true;private boolean isShowlevel3 = true;private boolean isShowmenu = true;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initView();initListener();}private void initView() {setContentView(R.layout.activity_rotate_menu);ButterKnife.bind(this);SpannableString title = new SpannableString("三級旋轉菜單");title.setSpan(new ForegroundColorSpan(Color.WHITE),0,title.length(),Spannable.SPAN_INCLUSIVE_EXCLUSIVE);ActionBar actionBar = getSupportActionBar();actionBar.setTitle(title);}private void initListener() {mIv_home.setOnClickListener(this);mIv_menu.setOnClickListener(this);btn_menu.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.iv_home:if (AnimUtil.animCount != 0){return;}if (isShowlevel2){int startOffset = 0;if (isShowlevel3){AnimUtil.closeMenu(mLevel3,startOffset);startOffset += 200;isShowlevel3 = false;}AnimUtil.closeMenu(mLevel2,startOffset);}else {AnimUtil.openMenu(mLevel2,0);}isShowlevel2 = !isShowlevel2;break;case R.id.iv_menu:if (AnimUtil.animCount != 0){return;}if (isShowlevel3){AnimUtil.closeMenu(mLevel3,0);}else {AnimUtil.openMenu(mLevel3,0);}isShowlevel3 = !isShowlevel3;break;case R.id.btn_menu:showMenu();break;}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_MENU){showMenu();return true;}return super.onKeyDown(keyCode, event);}private void showMenu(){if (isShowmenu){int startOffset = 0;if (isShowlevel3){AnimUtil.closeMenu(mLevel3,startOffset);isShowlevel3 = false;startOffset += 200;}if (isShowlevel2){AnimUtil.closeMenu(mLevel2,startOffset);isShowlevel2 = false;startOffset += 200;}AnimUtil.closeMenu(mLevel1,startOffset);}else {AnimUtil.openMenu(mLevel1,0);AnimUtil.openMenu(mLevel2,200);isShowlevel2 = true;AnimUtil.openMenu(mLevel3,400);isShowlevel3 = true;}isShowmenu = !isShowmenu;} }AnimUtil.java
public class AnimUtil {public static int animCount = 0;//記錄當前執行的動畫數量public static void closeMenu(View view, int startOffset) {view.setPivotX(view.getWidth()/2);view.setPivotY(view.getHeight());//view.invalidate();view.animate().rotation(-180).setDuration(500).setListener(mListener).setStartDelay(startOffset).start();}public static void openMenu(View view, int startOffset) {view.setPivotX(view.getWidth()/2);view.setPivotY(view.getHeight());//view.invalidate();view.animate().rotation(0).setDuration(500).setListener(mListener).setStartDelay(startOffset).start();}static AnimatorListener mListener = new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {animCount++;}@Overridepublic void onAnimationEnd(Animator animation) {animCount--;}}; }代碼:https://github.com/JackChen1999/RotateMenu
總結
以上是生活随笔為你收集整理的自定义控件:旋转菜单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java高并发编程:使用JDK5中同步技
- 下一篇: 自定义控件:水波纹