2012年3月13日 星期二

Android學習_PopupWindow的使用

轉換回Android後,花最多時間的就是PopupWindow,因為在iOS中UIPopoverController是一個很完整的控制項,不但可以任意放入想要顯示的UIView、也可以選擇要出現的位置、視窗的箭頭方向、視窗的標題,甚至可以放入UINavigationController快速地進行不同View的切換。


註:iOS中我想要不點掉PopoverView的情況下,操作後面的內容,似乎無法做到?但整體還是瑕不掩瑜。

對應Android則為PopupWindow,在官網的說明為:"A popup window that can be used to display an arbitrary view. The popup windows is a floating container that appears on top of the current activity.",總之,他說PopupWindow是一個可以隨心所欲放入任何View的浮動視窗,所以我們可以馬上實做一個。

1. 建立一個想要在PopupWindow顯示的Layout
很單純的只在裡面加入一個Button與ListView。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#2B5D0E"
>
<Button android:id="@+id/btn_close"
android:text="@string/closepop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

<EditText android:id="@+id/lab"
android:hint="@string/lab_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

<ListView
android:id="@+id/lv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
></ListView>
</LinearLayout>


2. 建立一個繼承於PopupWindow的類別

並且在按鈕的OnClickListener加上解散的語法

dismiss();


3. 把PopupWindow叫出來

private void pop()
{
Context mContext = OlaTeach_popupwindowActivity.this;
LayoutInflater mLayoutInflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
View v_pop = mLayoutInflater.inflate(R.layout.pop_identifyinfo, null);
popup_ola popOlaWindow = new popup_ola(v_pop, 400,600,this);
popOlaWindow.setContentView(v_pop);
popOlaWindow.update();
popOlaWindow.showAtLocation(findViewById(R.id.activityRoot), Gravity.LEFT|Gravity.TOP, 10, 70);
}

註:PopupWindow不能在onCreate的時候叫出來,若是想達到類似的效果,必須由一個Thread來執行。

我們可以在介面上以一個佈滿整個APP的按鈕呼叫剛剛建立的PopupWindow。

按一下按鈕,可以看到PopupWindow出現在視窗上。

這時心中應該是覺得,YES,完成了?

但是去點擊close的按鈕時,卻會發現Popup雖然有正確的執行OnClickListener內的程式碼,但卻沒有做出應有的動畫反應,也就是沒有變色;ListView也會有一樣的情況,雖然可以上下滾動,但點擊時Item卻不會變色;至於EditText更慘,點擊後不會出現小鍵盤供使用者輸入。

*這時點擊下方的大按鈕,可以發現仍舊可以操作。

---------------------------------------------------
**解決PopupWindow內Button、ListView沒對應動畫,EditText不會彈出小鍵盤

popOlaWindow.setFocusable(true);

很直觀的,我們設定Focus為true,讓Android知道應該要將焦點擺在PopupWindow上。
可以看到,Button與ListView現在都會顯示對應的動畫EditText也可以順利彈出虛擬鍵盤,但是PopupWindow卻沒辦法像一般認知的點擊外部時,直接隱藏,而必須要點擊close按鈕。

**解決點擊PopupWindow外時,自動隱藏PopupWindow

popOlaWindow.setBackgroundDrawable(new BitmapDrawable());

增加這句完全搞不懂原因的設定,將可以讓PopupWindow在兩種情況下隱藏:
1. 點擊PopupWindow以外的地方。
2. 點擊左下的Back按鈕。

正當高興時,點擊EditText跳出的虛擬鍵盤卻會造成另一個問題。
虛擬鍵盤的彈出,讓PopupWindow超出了螢幕的範圍,造成要輸入的EditText看不到。

**解決點擊EditText,PopupWindow移動問題。
最正確的解決方法應該是,當鍵盤彈出時,動態改變PopupWindow的高度,待鍵盤收起時再恢復(也就是iOS內建的解決方案),但查了很多方式,都沒辦法完整的實現,所以後來採取其他方式。
1. 把PopupWindow變矮:雖然感覺很蠢,但卻是最直接的方法。

2. 讓虛擬鍵盤彈出時,PopupWindow不要上移。

popOlaWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

這也是我後來使用的方式。

------完整的呼叫程式碼-------

private void pop()
{
Context mContext = OlaTeach_popupwindowActivity.this;
LayoutInflater mLayoutInflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
View v_pop = mLayoutInflater.inflate(R.layout.pop_identifyinfo, null);
popup_ola popOlaWindow = new popup_ola(v_pop, 400,600,this);
popOlaWindow.setFocusable(true);
popOlaWindow.setBackgroundDrawable(new BitmapDrawable());
popOlaWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
popOlaWindow.setContentView(v_pop);
popOlaWindow.update();
popOlaWindow.showAtLocation(findViewById(R.id.activityRoot), Gravity.LEFT|Gravity.TOP, 10, 70);
}


最後,我們可以稍微套一下版,做一個簡單的箭頭,

就可以完成擁有幾個特性的PopupWindow:
1. 可以放置其他View進去
2. 點擊PopupWindow內的元件可以做出應有之反應
3. 點擊外部或是返回鍵將可隱藏PopupWindow
4. 鍵盤的彈跳不會影響PopupWindow的操作
*. 但是沒辦法完成,PopupWindow一切操作正常,又可以操作後面控制項的方式。

如果照著做,恭喜你,關於PopupWindow的相關測試就花了三個整天。

後記--------------------------------------
這樣子的PopupWindow已經可符合許多應用的場合,但觀察Android內的Google Map可以發現:

在地圖上呈現了一個類似PopupWindow的效果,但除了可以正確的操作上面的控制項外,也可以同時操作地圖。該介面的呈現,很明顯不是由繼承PopupWindow的類別所完成,而必須要重新以其他方式撰寫,所以雖然好不容易PopupWindow已經可以使用,最後還是重新手工打造,做出類似PopupWindow卻可以同時操作地圖的視窗。

效果:

7 則留言:

KURO 提到...

ola你好,我最近也有在試驗popupWindow
不知道你是否有遇過editText在PopUpwindows裡時
editText的OnLongClick事件不會觸發的這件事
就是長按時不會跳出一般編修文字的工具bar(剪下、複製、輸入法)
我自己實驗過後 是可以幫editText加OnLongClickListener然後再裡面寫跳出切換輸入法的功能,可是要呼叫編修文字的工具bar的話
就不知道要從何下手了不知你是否有解決方法呢

ola的家 提到...

我沒有測試過編修文字工具這塊,後來使用popupWindow都偏向於彈出選單的應用,也就是類似彈出編修文字工具(popupWindow自己就是那個工具)的方式,至於需要輸入內容或是比較複雜的操作,都是直接使用Dialog(其實與popupWindowvu性質相近,彈出後只操作自己,底下的物件不可操作),若是需要操作底下的內容,我是利用addView的方式將自己做的View直接覆蓋於RootView上。

匿名 提到...

大大想請問一下您最後說到
"類似PopupWindow卻可以同時操作地圖的視窗"
實際的做法要怎麼弄?

ola的家 提到...

我是利用addView的方式將自己做的View直接覆蓋於RootView上(rootview指的是你要加上view的那個activity最外層的Layout),如果要子view跟母view傳值可以參考:http://wangshifuola.blogspot.tw/2012/03/androidimitate-ioss-delegate-callback.html

匿名 提到...

謝謝,我了解了,當初本來想要用PopupWindow做出類似功能,結果沒辦法同時操作2個視窗,轉而想到用系統級的視窗(type=2002),可是導致加入的視窗無法自動與activity同時結束,煩惱了好一陣子,沒想到原來是我跳太大,直接跳過RootView.....

Ming Wu 提到...
作者已經移除這則留言。
匿名 提到...

請問一下popwindowup 有個語法可以提供點擊popwindow後
popwindow之外的範圍可以變淡色點,或變半透明

張貼留言