Android之Window、WindowManager(一):PopupWindow添加浮动窗口内部过程

来源:互联网 发布:邓亚萍 20亿 知乎 编辑:程序博客网 时间:2024/05/01 17:06

    通常情况下,如果想显示一个界面,首先想到的是建立一个Activity,然后所有的操作在Activity里面实现,或者是一个Dialog或者Toast。本文通过对PopupWindow的实现过程解析,指出添加界面的另外一种方式:直接用WindowManager显示添加或删除View的过程。


一、PopupWindow向Window添加视图的过程

PopupWindow是最简单的浮动窗口,显示在当前activity窗口上面,显示一个PopupWindow的代码非常简单,如下:

<span style="font-size:18px;">//弹出pop窗口private void PopView(){View view =View.inflate(this, R.layout.list_item, null);PopupWindow popup=new PopupWindow(view,LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT,true);popup.setBackgroundDrawable(getResources().getDrawable(R.drawable.ic_launcher));popup.showAsDropDown(bt);}</span>

很显然,new PopupWindo()以及setBackgroundDrawabl()都只是做了一些初始化的工作,Android加载PopupWindow浮动窗口的所有逻辑过程和工作,都是在showAsDropDown()方法中进行的,以此为突破口,可以逐级分析浮动窗口内部加载过程。

<span style="font-size:18px;">    /**     * Display the content view in a popup window at the specified location. If the popup window cannot fit on screen, it will be clipped. See android.view.WindowManager.LayoutParams for more information on how gravity and the x and y parameters are related.     */    public void showAtLocation(View parent, int gravity, int x, int y) {        showAtLocation(parent.getWindowToken(), gravity, x, y);    }</span>

parent一般是当前activity的view,主要是传递popupwindow当前activity界面的IBinder,继续往下走:


    public void showAtLocation(IBinder token, int gravity, int x, int y) {        WindowManager.LayoutParams p = createPopupLayout(token);        ...                invokePopup(p);    }


核心方法只有两个:createPopupLayout和invokePopup。createPopupLayout主要是设置PopupWindow相对窗体的LayoutParams:

    private WindowManager.LayoutParams createPopupLayout(IBinder token) {        // 生成layout parameters        WindowManager.LayoutParams p = new WindowManager.LayoutParams();        // 默认gravity在左上角        // X、Y分别是相对偏移量        p.gravity = Gravity.START | Gravity.TOP;        p.width = mLastWidth = mWidth;        p.height = mLastHeight = mHeight;        //必须设置背景        if (mBackground != null) {            p.format = mBackground.getOpacity();        } else {            p.format = PixelFormat.TRANSLUCENT;        }        //        p.flags = computeFlags(p.flags);        //        p.type = mWindowLayoutType;        //        p.token = token;        //        p.softInputMode = mSoftInputMode;        p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));        return p;    }

invokePopup最终调用WindowManager.addView()方法,将PopupWindow中的内容视图及其LayoutParams属性,添加到Window:

    /**     * 将PopupWindow的内容视图添加到window manger     */    private void invokePopup(WindowManager.LayoutParams p) {        if (mContext != null) {            p.packageName = mContext.getPackageName();        }        mPopupView.setFitsSystemWindows(mLayoutInsetDecor);        setLayoutDirectionFromAnchor();        mWindowManager.addView(mPopupView, p);  //将view添加到window中,p是相对window的属性    }

分析到这里,我们可以总结一下:

Android平台是一个又一个的Activity组成的,每一个Activity有一个或者多个View构成。通常情况下,如果想显示一个界面,首先想到的是建立一个Activity,然后所有的操作在Activity里面实现,或者是一个Dialog或者Toast。这种方式固然简单,但是在有些情况下,我们要求的只是简单的显示,用Activity显然是多余,这个时候,我们如何处理呢?

通过PopupWindow的实现过程,我们了解,原来整个Android的窗口机制是基于一个叫做WindowManager,这个接口可以添加view到屏幕,也可以从屏幕删除view。它面向的对象一端是屏幕,另一端就是View,直接忽略我们以前的Activity或者Dialog之类的东东。其实我们的Activity或者Diolog底层的实现也是通过WindowManager,这个WindowManager是全局的,整个系统就是这个唯一的东东。它是显示View的最底层了。
所以,PopupWindow的过程,其实就是直接用WindowManager显示View的过程。


二、模仿PopupWindow的一个自定义CustomPopupWindow

这个Demo仅仅实现了showAtLocation、createPopupLayout、invokePopup三个方法,就可以实现简单的PopupWindow效果。

自定义CustomPopupWindow控件类:

package com.custom.view;import android.content.Context;import android.graphics.PixelFormat;import android.os.IBinder;import android.view.Gravity;import android.view.View;import android.view.WindowManager;import android.widget.PopupWindow;public class CustomPoupuWindow {private int height;private int width;private View contView;private Context context;private WindowManager wm;public CustomPoupuWindow(View contView,int width,int height){this.contView=contView;this.context=contView.getContext();this.width=width;this.height=height;wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);}//在指定位置显示popupwindow    public void showAtLocation(int gravity, int x, int y) {        WindowManager.LayoutParams p = createPopupLayout();        //p.windowAnimations = computeAnimationResource();               //preparePopup(p);        if (gravity == Gravity.NO_GRAVITY) {            gravity = Gravity.TOP | Gravity.START;        }        p.gravity = gravity;        p.x = x;        p.y = y;        invokePopup(p);    }            //设置popupWindow的contView相对window的属性    private WindowManager.LayoutParams createPopupLayout() {        //获取WindowManager            // generates the layout parameters         WindowManager.LayoutParams p = new WindowManager.LayoutParams();                // these gravity settings put the view at the top left corner of the        // screen. The view is then positioned to the appropriate location        // by setting the x and y offsets to match the anchor's bottom        // left corner        p.gravity = Gravity.START | Gravity.TOP;        p.width = width;        p.height = height;        /*        if (mBackground != null) {            p.format = mBackground.getOpacity();        } else {            p.format = PixelFormat.TRANSLUCENT;        }*/                p.format = PixelFormat.TRANSLUCENT;                p.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;        p.type = WindowManager.LayoutParams.TYPE_PHONE;        //p.token = token;        //p.softInputMode = mSoftInputMode;        p.setTitle("CustomPopupWindow:" );        return p;    }    private void invokePopup(WindowManager.LayoutParams p) {        //mPopupView.setFitsSystemWindows(mLayoutInsetDecor);        //setLayoutDirectionFromAnchor();        wm.addView(contView, p);    }}

调用CustomPopupWindow过程

private void CustomPopView(){View view =View.inflate(this, R.layout.list_item, null);CustomPoupuWindow popup=new CustomPoupuWindow(view,LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);popup.showAtLocation( Gravity.NO_GRAVITY, 0, 100);}

自定义XML文件:

<?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="horizontal" >    <TextView         android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/file_name"        android:layout_gravity="center_vertical"        android:layout_marginLeft="20dp"        android:background="@drawable/ic_launcher"        />    </LinearLayout>

效果图:







1 0