android PopupWindow模擬Windows開始菜單顯示消失效果

android系統給我提供的菜單不盡人意,大多數情況下我們都是根據需求,重寫系統的菜單,達到想要的效果,下面就用PopupWindow兩種方式重寫系統菜單效果。

package com.jacp.app;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;

public class PopupWindowActivity extends Activity implements OnClickListener {
	
	private PopupWindow mLeftMenu;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 註冊按鈕事件
        findViewById(R.id.left).setOnClickListener(this);
        findViewById(R.id.right).setOnClickListener(this);
    }

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.left:
			showLeftMenu();
			break;
		case R.id.right:
			startActivity(new Intent(this, PopupWindowSecondActivity.class));
			break;
		}
	}
	
	/**
	 * 顯示菜單
	 * @return 如果菜單顯示則返回true,否則返回false
	 */
	private boolean showLeftMenu() {
		if (hideLeftMenu())
		{
			return false;
		}
		LinearLayout leftLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.menu_list, null);
		// 當菜單出現時,最外層佈局接受Touch事件
		leftLayout.setOnTouchListener(new OnTouchListener()
		{
			@Override
			public boolean onTouch(View v, MotionEvent event)
			{
				hideLeftMenu();
				return false;
			}
		});
		
		ListView list = (ListView) leftLayout.findViewById(R.id.item_list);
		String[] data = getResources().getStringArray(R.array.menu_item);
		int[] res = new int[data.length];
		for (int i = 0, length = data.length; i < length; i++)
		{
			res[i] = R.drawable.icon;
		}
		list.setAdapter(new PopupAdapter(this, data, res, R.layout.menu_item, R.id.item_image, R.id.item_text));
		list.setOnItemClickListener(new OnItemClickListener()
		{
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id)
			{
				hideLeftMenu();
			}
		});
		// 當菜單出現時焦點會落在ListView上
		list.setOnKeyListener(new OnKeyListener()
		{
			@Override
			public boolean onKey(View v, int keyCode, KeyEvent event)
			{
				// 此方法會調兩次,一次Down和一次Up
				// 所以此處要攔截
				if (event.getAction() == KeyEvent.ACTION_DOWN)
				{
					switch (keyCode)
					{
					case KeyEvent.KEYCODE_MENU:
						return hideLeftMenu();
					case KeyEvent.KEYCODE_BACK:
						return hideLeftMenu();
					}
				}
				return false;
			}
		});
		
		// 設置菜單屬性
		mLeftMenu = new PopupWindow(leftLayout, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
		mLeftMenu.setWidth(150);
		// 設置菜單上是否可以聚焦
		mLeftMenu.setFocusable(true);
		// 當菜單出現時菜單以外的區域是否接受點擊事件
		mLeftMenu.setOutsideTouchable(true);
		// 設置菜單顯示的位置
		mLeftMenu.showAtLocation(leftLayout, Gravity.BOTTOM | Gravity.LEFT, 0, 80);
		return true;
	}
	
	/**
	 * 隱藏菜單
	 * @return 如果菜單隱藏則返回true,則否返回false
	 */
	private boolean hideLeftMenu()
	{
		if (null != mLeftMenu && mLeftMenu.isShowing())
		{
			mLeftMenu.dismiss();
			mLeftMenu = null;
			return true;
		}
		return false;
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		switch (keyCode) 
		{
		case KeyEvent.KEYCODE_BACK:
			hideLeftMenu();
			break;
		case KeyEvent.KEYCODE_MENU:
			showLeftMenu();
			break;
		}
		return super.onKeyDown(keyCode, event);
	}
	
}
其中有三點要注意:

1.PopupWindow的參數設置

// 設置菜單上是否可以聚焦
mLeftMenu.setFocusable(true);
// 當菜單出現時菜單以外的區域是否接受點擊事件
mLeftMenu.setOutsideTouchable(true);
2.當PopupWindow顯示時焦點會落在其中,所以監聽事件的是它裏面的View,如例中的Touch事件。按鍵事件是佔整個View的ListView監聽的。很奇怪的是不是裏面的LinearLayout,如果高人知道,請在下面留言,小弟不甚感激。

3.最後一點是注意按鍵事件。例子中重寫了Activity的OnKeyDown事件,而在ListView中的OnKey方法必須攔截KeyDown事件。如果不攔截菜單出現就會消失,因爲OnKey方法會因Down和Up調用兩次;如果攔截的是Up事件,情況也是一樣的,因爲當菜單沒有出現按Menu鍵時,先執行Activity裏面的onKeyDown事件,菜單出現。注意菜單出現是Down事件,那Up呢?正好當PopupWindow出現時,ListView繼續執行事件,會執行OnKey事件,如果是攔截的Action是Up則會執行,所以整個事件會兩個地方執行。有人說在Activity裏面的OnKeyDown事件return true進行攔截不就行了,這樣事件就不會執行到PopupWindow上面,這樣想沒有錯,如果真的是那樣,那按Back鍵後,整個Activity就不會退出。


以上是第一種方法,下面請看第二種方法:

package com.jacp.app;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;

public class PopupWindowSecondActivity extends Activity implements OnClickListener 
{
	
	private PopupWindow mRightMenu;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		findViewById(R.id.left).setVisibility(View.GONE);
        findViewById(R.id.right).setOnClickListener(this);
	}

	@Override
	public void onClick(View v)
	{
		switch (v.getId())
		{
		case R.id.right:
			showRightMenu();
			break;
		}
	}
	
	/**
	 * 顯示菜單
	 * @return 如果菜單顯示返回true,否則返回false
	 */
	private boolean showRightMenu()
	{
		if (hideRightMenu())
		{
			return false;
		}
		
		// 加載佈局內容
		LinearLayout centerLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.menu_list, null);
		centerLayout.setOnTouchListener(new OnTouchListener()
		{
			@Override
			public boolean onTouch(View v, MotionEvent event)
			{
				hideRightMenu();
				return false;
			}
		});
		ListView list = (ListView) centerLayout.findViewById(R.id.item_list);
		String[] data = getResources().getStringArray(R.array.menu_item);
		int[] res = new int[data.length];
		for (int i = 0, length = data.length; i < length; i++)
		{
			res[i] = R.drawable.icon;
		}
		list.setAdapter(new PopupAdapter(this, data, res, R.layout.menu_item, R.id.item_image, R.id.item_text));
		list.setOnKeyListener(new OnKeyListener()
		{
			@Override
			public boolean onKey(View v, int keyCode, KeyEvent event)
			{
				if (event.getAction() == KeyEvent.ACTION_DOWN)
				{
					switch (keyCode)
					{
					case KeyEvent.KEYCODE_MENU:
						showRightMenu();
						break;
					}
				}
				return false;
			}
		});
		
		// 設置菜單屬性
		mRightMenu = new PopupWindow(this);
		mRightMenu.setContentView(centerLayout);
		mRightMenu.setWidth(150);
		mRightMenu.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
		// 設置菜單上是否可以聚焦
		mRightMenu.setFocusable(true);
		// 當菜單出現時菜單以外的區域是否接受點擊事件
		mRightMenu.setOutsideTouchable(true);
		mRightMenu.showAtLocation(centerLayout, Gravity.RIGHT | Gravity.BOTTOM, 0, 80);
		
		return true;
	}

	/**
	 * 隱藏菜單
	 * @return 如果菜單隱藏則返回true,否則返回false
	 */
	private boolean hideRightMenu()
	{
		if (null != mRightMenu && mRightMenu.isShowing())
		{
			mRightMenu.dismiss();
			mRightMenu = null;
			return true;
		}
		
		return false;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		switch (keyCode)
		{
		case KeyEvent.KEYCODE_BACK:
			hideRightMenu();
			break;
		case KeyEvent.KEYCODE_MENU:
			showRightMenu();
			break;
		}
		return super.onKeyDown(keyCode, event);
	}
}

這兩個的不同點有二:

1.創建PopupWindow時傳的參數不同;

2.第二種方式中的ListView的onKey事件中少了一個KEYCODE_MENU,而達到了和第一種同樣的效果。

感覺這樣的區別很奇怪,爲此還看了一下源代碼,但沒有找到答案。唯一的就是想知道當前的Activity和View.getContext()有啥區別,因爲在源碼中如果沒有傳Context過去,Context就是通過View.getContext()獲得到的。望大神指點!

示例下載地址:http://download.csdn.net/source/3555098

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章