WMS学习从悬浮窗的添加来看窗口的add和update

Posted aaajj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WMS学习从悬浮窗的添加来看窗口的add和update相关的知识,希望对你有一定的参考价值。

这里我们从一个悬浮窗应用来查看WindowManager的addView使用,从这里作为突破口来认识窗口的添加,和窗口的位置大小更新方法updateViewLayout,使用WindowManager的addView方法来添加窗口非常的直观,因为Activity的显示中,窗口的添加调用封装的太深了,

以下代码参考自https://blog.csdn.net/liufish992/article/details/122338344

private void initUi()

DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();

int width = metrics.widthPixels;

int height = metrics.heightPixels;

windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);

floatView = (ViewGroup) inflater.inflate(R.layout.float_layout, null);

int layoutType;

layoutType = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

WindowManager.LayoutParams floatLp = new WindowManager.LayoutParams(

(int) (width * (0.4f)),

(int) (height * (0.3f)),

layoutType,

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,

PixelFormat.TRANSLUCENT

);

mLp = floatLp;

floatLp.gravity = Gravity.LEFT;//Gravity.CENTER;

floatLp.x = 0;

floatLp.y = 0;

windowManager.addView(floatView, floatLp);

位置变化设置

void move()

mLp.x = 100;

mLp.y = 100;

windowManager.updateViewLayout(floatView, mLp);

使用dumpsys window windows查看

悬浮窗的信息,ty=APPLICATION_OVERLAY,appop=SYSTEM_ALERT_WINDOW

Window #8 Windowd3f21db u0 com.example.myapplication:

mDisplayId=0 rootTaskId=1 mSession=Sessione489994 10075:u0a10159 mClient=android.os.BinderProxy@1cdc2d5

mOwnerUid=10159 showForAllUsers=false package=com.example.myapplication appop=SYSTEM_ALERT_WINDOW

mAttrs=(100,100)(432x646) gr=CENTER sim=adjust=pan ty=APPLICATION_OVERLAY fmt=TRANSLUCENT

fl=NOT_FOCUSABLE HARDWARE_ACCELERATED

pfl=USE_BLAST INSET_PARENT_FRAME_BY_IME

bhv=DEFAULT

fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR

Requested w=432 h=646 mLayoutSeq=1429

mBaseLayer=111000 mSubLayer=0 mToken=WindowTokenbb70aea type=2038 android.os.BinderProxy@1cdc2d5

Activity的信息,ty=BASE_APPLICATION

Window #11 Windowe656d83 u0 com.example.myapplication/com.example.myapplication.MainActivity:

mDisplayId=0 rootTaskId=34 mSession=Sessione489994 10075:u0a10159 mClient=android.os.BinderProxy@3ae1c32

mOwnerUid=10159 showForAllUsers=false package=com.example.myapplication appop=NONE

mAttrs=(0,0)(fillxfill) sim=adjust=pan forwardNavigation ty=BASE_APPLICATION wanim=0x10302fd

fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS

pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND USE_BLAST FIT_INSETS_CONTROLLED

bhv=DEFAULT

fitSides=

Requested w=1080 h=2220 mLayoutSeq=1429

mBaseLayer=21000 mSubLayer=0 mToken=ActivityRecord34f5722 u0 com.example.myapplication/.MainActivity t34

mActivityRecord=ActivityRecord34f5722 u0 com.example.myapplication/.MainActivity t34

布局参数LayoutParams中最直观的就是窗口位置和大小,如果是全屏的,就不像是悬浮窗了。

Activity的显示中,也是会走到WindowManager的addView这里。

WindowManager的addView方法调用WindowManagerGlobal的addView,里面创建了ViewRootImpl对象,

注意这里布局参数LayoutParms的使用,ViewRootImpl在绘图时,使用了这个参数来构造悬浮窗。

修改窗口位置的调用updateViewLayout调用WindowManagerGlobal 对应的方法 updateViewLayout

// WindowManagerGlobal.java:

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 设置新的 LayoutParams

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);

通过 root.setLayoutParams(wparams, false) 方法,更新了 ViewRootImpl 的 LayoutParams

在 ViewRootImpl 的 setLayoutParams 方法中,会通过 scheduleTraversals 方法来对 View 重新布局和绘制,包括测量、布局、重绘这三个过程。

// ViewRootImpl.java:

void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView)

scheduleTraversals();

scheduleTraversals 调用PerformTraversals, 又调用relayoutWindow方法,

应用程序调用relayoutWindow来请求WMS为其创建一个Surface对象。frameworks\\base\\core\\Java\\android\\view\\ViewRootImpl.Java

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,

boolean insetsPending) throws RemoteException

float appScale = mAttachInfo.mApplicationScale;

boolean restore = false;

if (params != null && mTranslator != null)

restore = true;

params.backup();

mTranslator.translateWindowLayout(params);

if (params != null)

if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);

mPendingMergedConfiguration.getMergedConfiguration().seq = 0;

//Log.d(mTag, ">>>>>> CALLING relayout");

if (params != null && mOrigWindowType != params.type)

// For compatibility with old apps, don't crash here.

if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH)

Slog.w(mTag, "Window type can not be changed after "

+ "the window is added; ignoring change of " + mView);

params.type = mOrigWindowType;

int relayoutResult = mWindowSession.relayout(

mWindow, mSeq, params,

(int) (mView.getMeasuredWidth() * appScale + 0.5f),

(int) (mView.getMeasuredHeight() * appScale + 0.5f),

viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,

mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,

mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,

mPendingMergedConfiguration, mSurface);//这里传入变量mSurface

mPendingAlwaysConsumeNavBar =

(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;

//Log.d(mTag, "<<<<<< BACK FROM relayout");

if (restore)

params.restore();

if (mTranslator != null)

mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);

mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);

mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);

mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);

mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);

return relayoutResult;

WMS服务在响应应用程序进程请求relayout调用时,首先在当前进程空间创建一个Surface对象,然后调用Session的relayout()函数进一步完成窗口布局过程,最后将WMS服务中创建的Surface返回给应用程序。

WMS中

frameworks\\base\\services\\core\\Java\\com\\android\\server\\wm\\WindowManagerService.Java

同时,WMS的处理中也调用addWindow方法创建了对应的WindowState窗口对象。

猜想:在多窗口调用中,设置的ActivityOptions的bounds信息,在调用流程中,会传递给LayoutParams来控制窗口的位置和大小。

Intent it = new Intent(MainActivity.this, MainActivity2.class);

ActivityOptions ops = ActivityOptions.makeBasic();

ops.setLaunchBounds(new Rect(100, 100, 600, 700));

startActivity(it, ops.toBundle());

参考资料:

【Android】Window 机制_android window_一场雪ycx的博客-CSDN博客

https://blog.csdn.net/yangwen123/article/details/80674965?spm=1001.2014.3001.5502

https://blog.csdn.net/liufish992/article/details/122338344

使用WindowManager添加View——悬浮窗口的基本原理

Android系统中的“窗口”类型虽然很多,但只有两大类是经常使用的:一是由系统进程管理的,称之为“系统窗口”;第二个就是由应用程序产生的,用于显示UI界面的“应用窗口”。如果大家熟悉WindowManagerService(窗口管理机制WMS)的话,那么一切都很简单。它是一个负责统筹管理所有窗口的一个服务,从始到终一直在运作。之所以扯上WMS,因为它才是大Boss,所有的窗口变化都要通知到它。而WindowManager虽然与它没有之间的关系,但是对它负责,所有信息会经过一定的途径传回到WMS中。额,跑题了,我们说的是WindowManager,它是一个接口类,它可以实现对view的管理,包括增加,更新和删除。

 

一、WindowManager

  1. 获取WindowManager
    wManager = (WindowManager) getApplicationContext().getSystemService(
                    Context.WINDOW_SERVICE);

    在Activity和Service中都可以直接使用这个方法来获得WindowManager。其getSystemService返回的是一个WindowManagerImpl对象,这是一个存在于本地进程中的一个对象。而事实是WindowManagerImpl继承了WindowManager,而WindowManger继承了ViewManager。

  2. 设置WindowManager.LayoutParams
    LayoutParams里面存放着的是窗口的属性,通过这个变量,可以为窗口赋予各式的属性。也可以改变它的属性值,来进行各种各样的操作,像悬浮窗口的拖动,拉伸等操作。
    详细的属性表在:
    http://www.cnblogs.com/shitianzeng/articles/2814050.html
  3. WindowManager的操作
    (1)窗口添加
    public void addView(View view, ViewGroup.LayoutParams params);

    (2)窗口更新

    public void updateViewLayout(View view, ViewGroup.LayoutParams params);

    (3)窗口删除

    public void removeView(View view);

    以上的三个方法都存在于ViewManager中。

二、悬浮窗实例
          例子设计:利用service打开悬浮窗,其中放着一个自定义的View,点击消除悬浮窗。

  1. 首先是Service,它是由Activity打开。先获得WindowManager,再配置属性
    技术图片
    public class WindowService extends Service implements OnClickListener {
    
        private WindowManager wManager;// 窗口管理者
        private WindowManager.LayoutParams mParams;// 窗口的属性
        private MyView myView;
        private boolean flag = true;
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public void onCreate() {
            // TODO Auto-generated method stub
            wManager = (WindowManager) getApplicationContext().getSystemService(
                    Context.WINDOW_SERVICE);
            mParams = new WindowManager.LayoutParams();
            mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示window
            mParams.format = PixelFormat.TRANSLUCENT;// 支持透明
            //mParams.format = PixelFormat.RGBA_8888;
            mParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 焦点
            mParams.width = 490;//窗口的宽和高
            mParams.height = 160;
            mParams.x = 0;//窗口位置的偏移量
            mParams.y = 0;
            //mParams.alpha = 0.1f;//窗口的透明度
            myView = new MyView(this);
            myView.setOnClickListener(this);
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // TODO Auto-generated method stub
            if (flag) {
                flag = false;
                wManager.addView(myView, mParams);//添加窗口
            }
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            // TODO Auto-generated method stub
            if (myView.getParent() != null)
                wManager.removeView(myView);//移除窗口
            super.onDestroy();
        }
    
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if (v.equals(myView)) {
                flag = true;
                if (myView.getParent() != null)
                    wManager.removeView(myView);//移除窗口
            }
        }
    
    }
    技术图片

     

  2. 一个自定义的view
    很早以前写的surfaceView,里面包括文字图片的两个显示动画
    技术图片
    技术图片
    @SuppressLint("WrongCall")
    public class MyView extends SurfaceView implements SurfaceHolder.Callback {
    
        private static int span = 5;
        private MyThread wtf;
        private Paint paint;
        int sleepSpan = 150; // 动画的时延ms
        Bitmap logo_s, logo_l; // logo图片引用
        int width_s; // 图片大小
        int height_s;
        float currentX_s; // 图片位置
        float currentY_s;
        float currentX_l; // 图片位置
        float currentY_l;
        private Rect src;
        private RectF dst;
        private int currentAlpha = 0;
    
        public MyView(Context context) {
            super(context);
    
            this.getHolder().addCallback(this);// 设置生命周期回调接口的实现者
            paint = new Paint();// 创建画笔
            paint.setAntiAlias(true);// 打开抗锯齿
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            logo_s = BitmapFactory
                    .decodeResource(getResources(), R.drawable.logo_s);
            logo_l = BitmapFactory
                    .decodeResource(getResources(), R.drawable.logo_l);
            // TODO Auto-generated method stub
            width_s = logo_s.getWidth();
            height_s = logo_s.getHeight();
            src = new Rect(0, 0, 0, height_s);
            // 大图片的位置
            currentX_s = 5;
            currentY_s = 5;
            dst = new RectF(currentX_s, currentY_s, currentX_s, currentY_s
                    + height_s);
            currentX_l = currentX_s + width_s - logo_l.getWidth();
            // 小图片的位置
            currentY_l = currentY_s + height_s;
            currentAlpha = 0;
            wtf = new MyThread();
            wtf.start();
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            // 绘制黑填充矩形清背景
            super.onDraw(canvas);
            
            paint.setAlpha(120);// 设置不透明度为255
            paint.setColor(Color.BLACK);// 设置画笔颜色
            canvas.drawColor(Color.BLACK);
            // 进行平面贴图
            if (logo_s == null || logo_l == null)
                return;
            src.right += span;
            dst.right += span;
            canvas.drawBitmap(logo_s, src, dst, paint);
            paint.setAlpha(currentAlpha);
            canvas.drawBitmap(logo_l, currentX_l, currentY_l, paint);
            // canvas.drawBitmap(bitmap, src, dst, paint);
            /*
             * Rect src = new Rect(x1, y2, cx1,cy1); Rect dst = new Rect(x2, y2,
             * cx2, cy2); 第一个矩形,是你想截取的bitmap里面的哪一段。 第二个矩形,是你想显示在屏幕上的什么位置。
             * 两个矩形可以不一样大小,在绘制的时候,会自动拉伸。
             */
        }
    
        class MyThread extends Thread {
            public void run() {
                SurfaceHolder mholder = MyView.this.getHolder();// 获取回调接口
                // 绘制tatans
                try {
                    sleep(500);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                for (int i = 0; i <= width_s / span; i++) {
                    Canvas canvas = mholder.lockCanvas();// 获取画布
                    try {
                        synchronized (mholder) // 同步
                        {
                            onDraw(canvas);// 进行
                        }
                        sleep(20);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        if (canvas != null) {
                            mholder.unlockCanvasAndPost(canvas);
                        }
                    }
    
                }
                // 绘制天坦智慧
                for (int i = 0; i <= 25; i++) {
                    currentAlpha = i * 10;
                    Canvas canvas = mholder.lockCanvas();// 获取画布
                    try {
                        synchronized (mholder) // 同步
                        {
                            onDraw(canvas);// 进行
                        }
                        sleep(25);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        if (canvas != null) {
                            mholder.unlockCanvasAndPost(canvas);
                        }
                    }
    
                }
            }
        }
    
    }
    技术图片
  3. Activity中,点击打开service
    技术图片
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
            Log.d("CPACM","onTouchEvent");
            Intent intent = new Intent();
            intent.setClass(this, WindowService.class);
            startService(intent);
            this.finish();
            return super.onTouchEvent(event);
        }
    技术图片
  4. manifest.xml
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 使用SYSTEM_ALERT_WINDOW时必须要加 -->

     

  5. 效果图
    技术图片  技术图片

三、结束语
学到越深,发现需要学的越多

 

 

========================================

 

作者:cpacm
出处:(http://www.cnblogs.com/cpacm/p/4087690.html

以上是关于WMS学习从悬浮窗的添加来看窗口的add和update的主要内容,如果未能解决你的问题,请参考以下文章

使用WindowManager添加View——悬浮窗口的基本原理

使用WindowManager添加View——悬浮窗口的基本原理

WmS详解之如何理解Window和窗口的关系?基于Android7.0源码

android 全局悬浮窗的使用以及权限

Android 悬浮窗的贴边移动及属性动画

Android Widget和悬浮窗 原理