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