Android從屏幕底部彈出PopupWin窗口

1、佈局文件

lay_menupopwin

<?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="100dp"
    android:background="@color/dot_trans_white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text2"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:text="@string/t_menu_logout"
        android:textColor="@color/red"
        android:gravity="center"
        android:textSize="@dimen/textsize_h4"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:background="@color/transparent"/>

    <TextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:text="@string/t_menu_cancel"
        android:textColor="@color/black"
        android:gravity="center"
        android:textSize="@dimen/textsize_h4"/>

</LinearLayout>

2、anim

bottom_enter.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    
    <translate
        android:fromYDelta="100%p"
        android:toYDelta="0"
        android:duration="200" />
    
    <alpha
        android:fromAlpha="0.7"
        android:toAlpha="1.0"
        android:duration="200" />
    
</set>

bottom_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    
    <translate
        android:fromYDelta="0"
        android:toYDelta="100%p"
        android:duration="200" />
    
    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0.5"
        android:duration="200" />

</set>

3、樣式style.xml

<!-- popupwin泡泡窗口,控制面板 -->
    <style name="popWinAnimBot" parent="android:Animation">
        <item name="@android:windowEnterAnimation">@anim/bottom_enter</item>
        <item name="@android:windowExitAnimation">@anim/bottom_out</item>
    </style>

4、java

/**
 * 彈出菜單
 * Created by xl on 2019/5/15.
 */
public class MenuPopupWin extends PopupWindow {

    private Context m_context;
    private TextView m_tvMenu1;
    private TextView m_tvMenu2;
    private View m_view;
    private Window m_window;

    public MenuPopupWin(Context context, final OnMenuItemClickListener listener) {
        super(context);
        this.m_context = context;
        // 初始化view
        m_view = LayoutInflater.from(context).inflate(R.layout.lay_menupopwin, null);
        m_tvMenu1 = m_view.findViewById(R.id.text1);
        m_tvMenu2 = m_view.findViewById(R.id.text2);
        m_tvMenu1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dismiss();
            }
        });
        m_tvMenu2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (listener != null) {
                    listener.onItemClick(view, 1);
                }
                dismiss();
            }
        });
        // 設置自定義PopupWindow的View
        this.setContentView(m_view);
        // 設置自定義PopupWindow彈出窗體的寬
        this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        // 設置自定義PopupWindow彈出窗體的高
        this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        // 設置自定義PopupWindow彈出窗體可點擊
        this.setFocusable(true);
        // 設置自定義PopupWindow彈出窗體動畫效果
        this.setAnimationStyle(R.style.popWinAnimBot);
        // 實例化一個ColorDrawable顏色爲半透明
        ColorDrawable dw = new ColorDrawable(0xb0000000);
        // 設置自定義PopupWindow彈出窗體的背景
        this.setBackgroundDrawable(dw);
        // popupwindow消失,恢復透明度
        this.setOnDismissListener(new OnDismissListener() {

            @Override
            public void onDismiss() {
                WindowManager.LayoutParams lp = m_window.getAttributes();
                lp.alpha = 1f;
                m_window.setAttributes(lp);
            }
        });
        // mMenuView添加OnTouchListener監聽判斷獲取觸屏位置如果在選擇框外面則銷燬彈出框
        m_view.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                int height = m_view.findViewById(R.id.lay_spinnerdlg).getTop();
                int y = (int) event.getY();
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (y < height) {
                        dismiss();
                    }
                }
                // setOutsideTouchable(true)則點擊PopupWindow之外的地方PopupWindow會消失
                return true;
            }
        });
    }

    /**
     * 顯示dlg,window用來設置背景變暗
     *
     * @param parent
     */
    public void show(View parent, Window window) {
        showAtLocation(parent, Gravity.BOTTOM, 0, 0);
        m_window = window;
        // 設置背景變暗
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.alpha = 0.5f;
        window.setAttributes(lp);
    }

    /**
     * 定義接口
     */
    public interface OnMenuItemClickListener {
        void onItemClick(View view, int position);
    }
}

5、activity調用

showMenuPopup(view);
/**
     * 彈出menu菜單
     */
    private void showMenuPopup(View view){
        MenuPopupWin menu = new MenuPopupWin(this, new MenuPopupWin.OnMenuItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {

            }
        });
        menu.show(view, getWindow());
    }

補充:

showAtLocation與showAsDropDown問題

showAsDropDown(View anchor, int xoff, int yoff)方法

這個方法沒有什麼好說的,在錨點anchor的正下方彈出popup,參數xoff和yoff分別是x軸和y軸的偏移量,偏移量是相對錨點anchor來說的,以anchor的左下角爲參考點;

但是,需要注意的是,Android 7.0版本之前,在指定位置彈出popupwindow用showAsDropDown(View anchor, int xoff, int yoff)毫無問題,但在android 7.0上,用showAsDropDown()就需要注意下面兩點了:

  • 如果指定 PopupWindow 的高度爲 MATCH_PARENT,調用 showAsDropDown(View anchor) 時,在 7.0 之前,會在錨點 anchor 下邊緣到屏幕底部之間顯示 PopupWindow;而在 7.0、7.1 系統上的 PopupWindow 會佔據整個屏幕(除狀態欄之外)。

  • 如果指定 PopupWindow 的高度爲自定義的值height,調用 showAsDropDown(View anchor)時, 如果 height > 錨點 anchor 下邊緣與屏幕底部的距離, 則還是會出現7.0、7.1上顯示異常的問題;否則,不會出現該問題。

所以在無法避免上述的兩個問題時,這時候就需要用showAtLocation()來處理可能出現的popup顯示異常問題。

showAtLocation(View parent, int gravity, int x, int y)

對showAtLocation()方法的很多解釋都是:“相對於父控件的位置,x和y爲偏移量。”這種解釋絕對是錯的離譜,而且很可笑,如果是這樣,那它和showAsDropDown()方法還有什麼異同,瞪大眼睛看看這個方法的參數,x不再是xoff,y也不再是yoff,從字面上來看也不是什麼所謂的偏移量吧,首先,這個方法是對於整個window的屏幕以座標來定位置的,不存在相對於某一個view,有相對也是相對整個屏幕,參數x和y是座標。

該方法的第一個參數是parent,類型是個View,這個參數名很讓人誤解,其實,並不是把PopupWindow放到這個parent裏,並不要求這個parent是一個ViewGroup。官方文檔對這個參數的解釋是“a parent view to get the token from”,這個parent的作用應該是調用其getWindowToken()方法獲取窗口的Token,所以,只要是該窗口上的控件就可以了。我個人開發中,一般是拿該parent在屏幕中的座標拿來作爲參考的,通過parent.getLocationInWindow(location)獲得parent在屏幕上的座標,其中location是一個大小爲2的int數組。

第二個參數很讓人理解爲對齊方式,當然不是,而是通過設置gravity來設置座標原點:

  • Gravity.TOP | Gravity.LEFT 以屏幕左上角爲座標原點
  • Gravity.BOTTOM | Gravity.RIGHT 以屏幕右下角爲座標原點
  • Gravity.LEFT 以屏幕左側,屏幕高度 1/2 處爲座標原點

以此類推,可根據實際情況是設置座標原點的位置。

設置了第二個參數,後面的參數x,y也就是相對於座標原點的位置了。

完!!!

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