前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(六十五)多样的菜单

Android开发笔记(六十五)多样的菜单

作者头像
aqi00
发布2019-01-18 14:02:44
1.3K0
发布2019-01-18 14:02:44
举报
文章被收录于专栏:老欧说安卓老欧说安卓

菜单Menu

Android的菜单分为两类:选项菜单和上下文菜单,默认使用选项菜单。菜单的布局文件存放在res/menu目录下,使用ADT新建一个Android工程,首页代码MainActivity中会自动生成onMenuOpened和onMenuItemSelected函数代码。 展示选项菜单的途径有三种: 1、按下菜单键; 2、在代码中手动打开选项菜单,即调用函数openOptionsMenu; 3、按下导航栏右侧溢出菜单按钮,溢出菜单参见《Android开发笔记(二十)顶部导航栏ActionBar》; 下面是选项菜单需要重写的方法: onMenuOpened : 在菜单弹出时调用,一般无需重写 onMenuItemSelected : 在菜单项选择时调用,查看该方法的源码,会发现该方法内部做分支处理,判断如果是选项菜单则调用onOptionsItemSelected,如果是上下文菜单则调用onContextItemSelected。一般无需重写 onCreateOptionsMenu : 在页面打开时调用,需要重写指定菜单项目 onOptionsItemSelected : 在选项菜单的菜单项选中时调用,需要重写对不同菜单项做分支处理 onPrepareOptionsMenu : 在准备打开选项菜单时调用,一般无需重写 onOptionsMenuClosed : 在选项菜单关闭时调用,一般无需重写

上下文菜单ContextMenu

上下文菜单类似于Windows上的右键菜单,只不过手机上没有鼠标右键,所以一般在某个控件被长按时弹出。 展示上下文菜单的途径有两种: 1、在某个控件被长按时弹出。通常在onStart函数中加入registerForContextMenu为指定控件注册上下文菜单,在onStop函数中加入unregisterForContextMenu为指定控件注销上下文菜单。 2、在代码中手动打开上下文菜单。先执行registerForContextMenu方法注册菜单,然后执行openContextMenu打开菜单,最后执行unregisterForContextMenu注销菜单。 下面是上下文菜单需要重写的方法: onCreateContextMenu : 控件长按后,准备打开上下文菜单时调用,需要重写指定菜单项目 onContextItemSelected : 在上下文菜单的菜单项选中时调用,需要重写对不同菜单项做分支处理 onContextMenuClosed : 在上下文菜单关闭时调用,一般无需重写

菜单的点击事件

为方便理清两种菜单的相互关系与调用流程,我们对各种菜单点击事件做了测试,下面是不同场景下的日志结果: 打开页面 01-08 15:46:31.309: D/MainActivity(8885): onCreateOptionsMenu 01-08 15:46:31.309: D/MainActivity(8885): onPrepareOptionsMenu 点击弹出选项菜单 01-08 15:47:06.369: D/MainActivity(8885): onMenuOpened 01-08 15:47:06.373: D/MainActivity(8885): onPrepareOptionsMenu 01-08 15:47:06.373: D/MainActivity(8885): onMenuOpened 点击选项菜单的某项 01-08 15:47:31.909: D/MainActivity(8885): onMenuItemSelected 01-08 15:47:31.909: D/MainActivity(8885): onOptionsItemSelected 长按弹出上下文菜单 01-08 15:48:31.337: D/MainActivity(8885): onCreateContextMenu 点击上下文菜单的某项 01-08 15:49:04.589: D/MainActivity(8885): onMenuItemSelected 01-08 15:49:04.589: D/MainActivity(8885): onContextItemSelected 01-08 15:49:04.589: D/MainActivity(8885): onContextMenuClosed 01-08 15:49:04.593: D/MainActivity(8885): onContextMenuClosed 从以上日志可以看出,选项菜单和上下文菜单的区别有: 1、单击菜单项(不管是选项菜单还是上下文菜单)都会先触发onMenuItemSelected,如果是选项菜单则再触发onOptionsItemSelected,如果是上下文菜单则再触发onContextItemSelected; 2、选项菜单在页面打开后就创建好,弹出选项菜单时只是把已创建好的菜单打开而已,但上下文菜单要在每次打开前才进行创建操作; 3、选中某个菜单项后,上下文菜单会调用onContextMenuClosed方法关闭整个菜单,而选项菜单只是在界面上消失,并未调用关闭菜单方法onOptionsMenuClosed;

弹窗PopupWindow

在实际开发中,Android自带的菜单显得朴素不够灵活,一个是位置固定,如选项菜单固定从页面底部弹出,溢出菜单固定从页面右上角弹出,上下文菜单固定显示在页面中央;另一个是样式固定,无法设置菜单背景,也无法设置其他的菜单显示元素(即使是简单显示左侧图标,也要通过反射机制调用MenuBuilder的setOptionalIconsVisible方法)。为解决以上不足,我们可利用弹窗PopupWindow来实现任意位置的菜单展示,以及可定制的菜单样式。 PopupWindow的机制是实现一个弹出框,其内容可以是任意布局的View,其页面悬浮在当前Activity页面之上。要让PopupWindow支持菜单,可在它的内部定义一个ListView,通过展示列表项和列表点击事件,从而实现悬浮菜单的效果。 下面是弹窗的常用方法: PopupWindow构造函数 : 可设置弹窗的视图内容、大小、是否获得焦点等等。 setContentView : 设置弹窗的视图内容 setWindowLayoutMode : 设置弹窗的宽和高。如想单独设置宽度可使用setWidth方法,如想单独设置高度可使用setHeight方法。 setFocusable : 设置是否获得焦点。如为true则弹窗以外区域不可点击,如为false则弹窗以外区域可以点击。 setBackgroundDrawable : 设置弹窗的背景。 setAnimationStyle : 设置弹窗弹出和缩回时的动画样式。 isShowing : 判断弹窗是否在展示中。 showAtLocation : 让弹窗在上级视图中的绝对坐标中展现。可设置对齐方式,以及横坐标与纵坐标上的绝对偏移。 showAsDropDown : 让弹窗在指定视图位置以下拉形式展现。可设置相对于指定视图的横坐标与纵坐标上的相对偏移。 dismiss : 关闭弹窗。 update : 更新弹窗。 setTouchInterceptor : 设置弹窗的触摸监听器。 setOnDismissListener : 设置弹窗的关闭监听器。 下面是弹窗的几个使用小技巧: 1、点击弹窗以外的区域,弹窗自动消失; 首先保证setFocusable设置为false(经测试setOutsideTouchable设置不管用);然后在Activity页面注册一个手势监听器OnGestureListener,重写onSingleTapUp方法加入弹窗关闭的代码;最后重写Activity页面dispatchTouchEvent方法,调用手势检测GestureDetector的onTouchEvent方法。 2、弹窗在弹出和消失时显示伸缩动画; 调用setAnimationStyle方法设置动画样式,该样式在styles.xml中定义,其中"android:windowEnterAnimation"项定义的是展示弹窗时的动画,"android:windowExitAnimation"项定义的是关闭弹窗时的动画。

代码示例

下面是菜单与弹窗的例子代码:

代码语言:javascript
复制
import java.util.ArrayList;

import com.example.exmmenu.adapter.MenuLeftAdapter;
import com.example.exmmenu.adapter.MenuPopAdapter;
import com.example.exmmenu.adapter.PopMenuItem;
import com.example.exmmenu.util.MetricsUtil;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.Toast;
import android.widget.PopupWindow.OnDismissListener;

public class MainActivity extends Activity implements OnClickListener, OnItemClickListener {

	private static final String TAG = "MainActivity";
	private Button btn_context_long;
	private PopupWindow popupWindow;
	private int[] mIconList = {R.drawable.ic_refresh, R.drawable.ic_search,
			R.drawable.ic_about, R.drawable.ic_quit};
	private String[] mTextList = {"刷新", "搜索", "关于", "退出"};
	private ArrayList<PopMenuItem> mTitleList = new ArrayList<PopMenuItem>();
	private GestureDetector mGesture;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button btn_option = (Button) findViewById(R.id.btn_option);
		Button btn_context = (Button) findViewById(R.id.btn_context);
		btn_option.setOnClickListener(this);
		btn_context.setOnClickListener(this);
		btn_context_long = (Button) findViewById(R.id.btn_context_long);
		Button btn_pop = (Button) findViewById(R.id.btn_pop);
		Button btn_pop_left = (Button) findViewById(R.id.btn_pop_left);
		btn_pop.setOnClickListener(this);
		btn_pop_left.setOnClickListener(this);

		mGesture = new GestureDetector(this, mGestureListener);
		for (int i=0; i<mIconList.length; i++) {
			mTitleList.add(new PopMenuItem(mIconList[i], mTextList[i]));
		}
	}
	
	@Override
	protected void onResume() {
		registerForContextMenu(btn_context_long);  
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		unregisterForContextMenu(btn_context_long); 
		super.onPause();
	}
	
	private void closePopWindow() {
        if (popupWindow != null && popupWindow.isShowing()) {
            popupWindow.dismiss();
            popupWindow = null;
        }
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_option) {
			openOptionsMenu();
		} else if (v.getId() == R.id.btn_context) {
		    registerForContextMenu(v);
		    openContextMenu(v);
		    unregisterForContextMenu(v);
		} else if (v.getId() == R.id.btn_pop) {
			View view = LayoutInflater.from(this).inflate(R.layout.menu_pop, null);
			ListView lv_menu_pop = (ListView) view.findViewById(R.id.lv_menu_pop);
			MenuPopAdapter adapter = new MenuPopAdapter(this, mTitleList);
			lv_menu_pop.setAdapter(adapter);
			lv_menu_pop.setOnItemClickListener(this);
			popupWindow = new PopupWindow(view, MetricsUtil.getPopWidth(this), 
					LayoutParams.WRAP_CONTENT);
			popupWindow.setFocusable(false);
			popupWindow.showAsDropDown(v, 10, 0);
		} else if (v.getId() == R.id.btn_pop_left) {
			View view = LayoutInflater.from(this).inflate(R.layout.menu_pop, null);
			ListView lv_menu_pop = (ListView) view.findViewById(R.id.lv_menu_pop);
			MenuLeftAdapter adapter = new MenuLeftAdapter(this, mTitleList);
			lv_menu_pop.setAdapter(adapter);
			lv_menu_pop.setOnItemClickListener(this);
			popupWindow = new PopupWindow(view, MetricsUtil.getPopWidth(this), 
					LayoutParams.MATCH_PARENT, false);
			popupWindow.setOnDismissListener(new OnDismissListener() {
				public void onDismiss() {
					Toast.makeText(MainActivity.this, "左侧菜单消失了", Toast.LENGTH_SHORT).show();
				}
			});
			//在窗口内部的空白处点击,也要关闭弹窗
			view.setOnTouchListener(new OnTouchListener() {
	            @Override
	            public boolean onTouch(View v, MotionEvent event) {
	            	closePopWindow();
	                return false;
	            }
	        });
			popupWindow.setAnimationStyle(R.style.AnimationFade);
			popupWindow.showAtLocation(v, Gravity.LEFT, 0, 0);
		}
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    	closePopWindow();
		PopMenuItem item = mTitleList.get(position);
		Toast.makeText(this, "您点击的菜单名称是"+item.text, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onMenuOpened(int featureId, Menu menu) {
		Log.d(TAG, "onMenuOpened");
		return super.onMenuOpened(featureId, menu);
	}
	
	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		Log.d(TAG, "onMenuItemSelected");
		return super.onMenuItemSelected(featureId, item);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		Log.d(TAG, "onCreateOptionsMenu");
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		Log.d(TAG, "onOptionsItemSelected");
		return super.onOptionsItemSelected(item);
	}
	
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		Log.d(TAG, "onPrepareOptionsMenu");
		return super.onPrepareOptionsMenu(menu);
	}
	
	@Override
	public void onOptionsMenuClosed(Menu menu) {
		Log.d(TAG, "onOptionsMenuClosed");
		super.onOptionsMenuClosed(menu);
	}

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
		Log.d(TAG, "onCreateContextMenu");
		getMenuInflater().inflate(R.menu.main, menu);
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		Log.d(TAG, "onContextItemSelected");
		return super.onContextItemSelected(item);
	}
	
	@Override
	public void onContextMenuClosed(Menu menu) {
		Log.d(TAG, "onContextMenuClosed");
		super.onContextMenuClosed(menu);
	}
	
	//在弹窗以外的区域点击,都关闭弹窗
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		mGesture.onTouchEvent(ev);
		return super.dispatchTouchEvent(ev);
	}

	private OnGestureListener mGestureListener = new OnGestureListener() {

		@Override
		public boolean onDown(MotionEvent e) {
			return false;
		}

		@Override
		public void onShowPress(MotionEvent e) {
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
        	closePopWindow();
			return false;
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			return false;
		}

		@Override
		public void onLongPress(MotionEvent e) {
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			return false;
		}
		
	};
	
}

点击下载本文用到的各种菜单样式的工程代码

点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年02月17日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 菜单Menu
  • 上下文菜单ContextMenu
  • 菜单的点击事件
  • 弹窗PopupWindow
  • 代码示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档