美文网首页
(八)理解Window和WindowManager

(八)理解Window和WindowManager

作者: YongtaoHuang | 来源:发表于2019-10-11 10:16 被阅读0次

    Android的所有的视图(Activity、Dialog、Toast等的View)都是通过Window来呈现的。
    Window实际是View的直接管理者。


    WindowManager.png

    8.1 Window和WindowManager

    如何使用一个WindowManager添加一个Window:
    WindowManager.addView(View v , WindowManager.LayoutParams lp) 。示例源码如下:首先添加权限:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    
    public class MainActivity extends Activity implements View.OnTouchListener {
    
        private Button mCreateWindowButton;
    
        private Button mFloatingButton;
        private WindowManager.LayoutParams mLayoutParams;
        private WindowManager mWindowManager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
        }
    
        private void initView() {
            mCreateWindowButton = (Button) findViewById(R.id.button1);
            mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        }
    
        public void onButtonClick(View v) {
            if (v == mCreateWindowButton) {
                mFloatingButton = new Button(this);
                mFloatingButton.setText("click me");
    
                mLayoutParams = new WindowManager.LayoutParams(
                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                        PixelFormat.TRANSPARENT);
                mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | LayoutParams.FLAG_NOT_FOCUSABLE
                        | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
                }else {
                    mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
                }
    
                mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
                mLayoutParams.x = 100;
                mLayoutParams.y = 300;
                mFloatingButton.setOnTouchListener(this);
                mWindowManager.addView(mFloatingButton, mLayoutParams);
            }
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int rawX = (int) event.getRawX();
            int rawY = (int) event.getRawY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    int x = (int) event.getX();
                    int y = (int) event.getY();
                    mLayoutParams.x = rawX;
                    mLayoutParams.y = rawY;
                    mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    break;
                }
                default:
                    break;
            }
    
            return false;
        }
    
        @Override
        protected void onDestroy() {
            try {
                mWindowManager.removeView(mFloatingButton);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            super.onDestroy();
        }
    }
    

    效果图:


    Window.jpg

    8.2 Window的内部机制

    Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl建立联系。
    WindowManager对Window主要有三大操作:添加、更新和删除。这三个方法主要是定义在ViewManager接口中:

    public interface ViewManager {
        public void addView(View view, ViewGroup.LayoutParams params);//添加过程
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新过程
        public void removeView(View view);//删除过程
    }
    

    WindowManager也是一个接口,继承了ViewManager接口:

    public interface WindowManager extends ViewManager {}
    

    WindowManagerImpl 实现了WindowManager:源码如下:核心还是添加、更新和删除3个方法。

    public final class WindowManagerImpl implements WindowManager{
            @Override
            public void addView(View view, ViewGroup.LayoutParams params){
                mGlobal.addView(view, params, mDisplay, mParentWindow);
            }
            
            @Override
            public void updateViewLayout(View view, ViewGroup.LayoutParams params){
                mGlobal.updateViewLayout(view, params);
            }
            
            @Override
            public void removeView(View view){
                mGlobal.removeView(view, false);
            }
    }
    

    WindowManagerImpl并没有直接实现Window的三大操作,而是交给了WindowManagerGlobal。WindowManagerGlobal以单例模式向外提供自己的实例:

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    

    8.2.1 Window的添加过程

    1、检查参数是否合法,如果是子Window那么还需要调整一些布局参数
    2、创建ViewRootImpl并将View添加到列表中
    3、通过ViewRootImpl来更新界面并完成Window的添加过程

        public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
            if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
            if (display == null) {
                throw new IllegalArgumentException("display must not be null");
            }
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } else {
                // If there's no parent, then hardware acceleration for this view is
                // set from the application's hardware acceleration setting.
                final Context context = view.getContext();
                if (context != null
                        && (context.getApplicationInfo().flags
                                & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                    wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
                }
            }
    
            ViewRootImpl root;
            View panelParentView = null;
    
            synchronized (mLock) {
                // Start watching for system property changes.
                if (mSystemPropertyUpdater == null) {
                    mSystemPropertyUpdater = new Runnable() {
                        @Override public void run() {
                            synchronized (mLock) {
                                for (int i = mRoots.size() - 1; i >= 0; --i) {
                                    mRoots.get(i).loadSystemProperties();
                                }
                            }
                        }
                    };
                    SystemProperties.addChangeCallback(mSystemPropertyUpdater);
                }
    
                int index = findViewLocked(view, false);
                if (index >= 0) {
                    if (mDyingViews.contains(view)) {
                        // Don't wait for MSG_DIE to make it's way through root's queue.
                        mRoots.get(index).doDie();
                    } else {
                        throw new IllegalStateException("View " + view
                                + " has already been added to the window manager.");
                    }
                    // The previous removeView() had not completed executing. Now it has.
                }
    
                // If this is a panel window, then find the window it is being
                // attached to for future reference.
                if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                        wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                    final int count = mViews.size();
                    for (int i = 0; i < count; i++) {
                        if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                            panelParentView = mViews.get(i);
                        }
                    }
                }
    
                root = new ViewRootImpl(view.getContext(), display);
    
                view.setLayoutParams(wparams);
    
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
    
                // do this last because it fires off messages to start doing things
                try {
                    root.setView(view, wparams, panelParentView);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
            }
        }
    
    

    8.2.2 Window的删除过程

        public void removeView(View view, boolean immediate) {
            if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
    
            synchronized (mLock) {
                int index = findViewLocked(view, true);
                View curView = mRoots.get(index).getView();
                removeViewLocked(index, immediate);
                if (curView == view) {
                    return;
                }
    
                throw new IllegalStateException("Calling with view " + view
                        + " but the ViewAncestor is attached to " + curView);
            }
        }
    
    

    8.2.3 Window的更新过程

        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
            if (view == null) {
                throw new IllegalArgumentException("view must not be null");
            }
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
    
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    
            view.setLayoutParams(wparams);
    
            synchronized (mLock) {
                int index = findViewLocked(view, true);
                ViewRootImpl root = mRoots.get(index);
                mParams.remove(index);
                mParams.add(index, wparams);
                root.setLayoutParams(wparams, false);
            }
        }
    
    

    8.3 Window的创建过程

    View是Android中得视图得呈现方式,但是View不能单独存在,它必须附着Window这个抽象得概念上马,因此有视图得地方就有Window。

    8.3.1 Activity的Window的创建过程

    步骤:
    1、如果没有DecorView,那么就创建它;
    2、将View添加到DecorView得mContentParent中;
    3、回调Activity得onContentChanged方法通知Activity视图已经发生改变。

    8.3.2 Dialog的Window的创建过程

    步骤:
    1、创建Window;
    2、初始化DecorView并将Dialog得视图添加到DecorView中,
    3、将DecorView添加到Window中显示。

    8.3.3 Toast的Window的创建过程

    Toast的Window的创建过程与Dialog的Window的创建过程类似,但是由于Toast具有定时取消的功能,所以系统采用了Handler。

    相关文章

      网友评论

          本文标题:(八)理解Window和WindowManager

          本文链接:https://www.haomeiwen.com/subject/anpqectx.html