欢迎访问皮皮网官网
皮皮网

【股票交易 app源码】【溯源码燕窝装盒】【量化做T源码】windowmanager 源码

时间:2024-11-19 07:42:34 分类:知识 来源:qt实现lzo源码

1.Framework——WMS之WindowManager(窗口管理服务)实战
2.❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️
3.android开发设置屏蔽录制
4.手机直播源码,设置透明背景(去掉蒙层)状态栏颜色不改变

windowmanager 源码

Framework——WMS之WindowManager(窗口管理服务)实战

       在Android应用中,WindowManager(窗口管理服务)作为显示View的基石,其重要性不言而喻。它是Activity、Dialog和Toast背后的股票交易 app源码底层支持,通过addView、removeView和updateViewLayout等方法,以及WindowManager.LayoutParams进行属性设置,实现界面元素的管理与显示。

       WMS作为系统服务的核心组件,与ActivityManagerService共同构成了框架业务的主导部分,其功能涵盖了视图管理的方方面面。从悬浮窗的实现到SYSTEM_ALERT_WINDOW权限的处理,都需要理解和使用WMS。例如,溯源码燕窝装盒悬浮窗的实现涉及OnTouch事件处理,包括获取坐标和处理返回值,而WindowManager的使用则通过LayoutParams类,尤其是Gravity属性来设置View的参考系和透明度。

       设置View透明度时,LayoutParams的alpha属性起到了关键作用,从1.0f的完全不透明到0.0f的完全透明,提供了丰富的动态效果。此外,对于WindowManager添加的窗口,还需要关注使用系统提供的动画属性,以及通过Handler更新UI和定时器实现动态展示。想要深入学习和掌握这些技术,可以获取《Framework精通手册》和《Frame Work源码解析手册》等学习资料,这些资源将为你提供详尽的量化做T源码核心技术点。

       总的来说,本文通过实践演示了如何简单实现一个悬浮窗,以及在framework的WMS机制中使用WindowManager的基本技巧。通过阅读和实践,你将对WindowManager有更深入的理解,并能更好地应用到自己的项目中。快来点击获取免费的学习资料,进一步提升你的框架技能吧。

❤️ Android 源码解读-从setContentView深入了解 Window|Activity|View❤️

       Android系统中,Window、Activity、View之间的关系是紧密相连且相互作用的。了解这三者之间的关系,有助于深入理解Android应用的渲染和交互机制。

       在Android中,医生辅助系统源码通常在创建Activity时会调用`setContentView()`方法,以指定显示的布局资源。这个方法主要作用是将指定的布局添加到一个名为`DecorView`的容器中,并最终将其显示在屏幕上。这一过程涉及到多个组件的交互,下面分步骤解析。

       在`Activity`类中,`setContentView()`方法调用`getWindow()`方法获取`Window`对象,而`Window`对象在`Activity`的`attach()`方法中被初始化。`Window`对象是一个抽象类,其默认实现为`PhoneWindow`,这是Android特定的窗口实现。

       `PhoneWindow`在创建时会通过`setWindowManager()`方法与`WindowManager`进行关联。`WindowManager`是系统级组件,用于管理所有的运势搭建源码演示窗口,包括窗口的创建、更新、删除等操作。`WindowManager`的管理最终由`WindowManagerService`(WMS)执行,这是一个运行在系统进程中的服务。

       在`PhoneWindow`中,`installDecor()`方法会初始化`DecorView`和`mContentParent`。`mContentParent`是一个`ViewGroup`,用于存放`setContentView()`传入的布局。通过`mLayoutInflater`的`inflate()`方法,将指定的布局资源添加到`mContentParent`中。

       `DecorView`是一个特殊的`FrameLayout`,包含了`mContentParent`。在完成布局的添加后,`DecorView`本身并没有直接与`Activity`建立联系,也没有被绘制到屏幕上显示。`DecorView`的绘制和显示发生在`Activity`的`onResume()`方法执行后,这时`Activity`中的内容才真正可见。

       当`Activity`执行到`onCreate()`阶段时,其内容实际上并没有显示在屏幕上,直到执行到`onResume()`阶段,`Activity`的内容才被真正显示。这一过程涉及到`ActivityThread`中的`handleResumeActivity()`方法,该方法会调用`WindowManager`的`addView()`方法,将`DecorView`添加到`WindowManagerService`中,完成`DecorView`的绘制和显示。

       `WindowManagerService`通过`addView()`方法将`DecorView`添加到显示队列中,并且在添加过程中,会创建关键的`ViewRootImpl`对象,进一步管理`DecorView`的布局、测量和绘制。`ViewRootImpl`会调用`mWindowSession`的`addToDisplay()`方法,将`DecorView`添加到真正的显示队列中。

       `mWindowSession`是`WindowManagerGlobal`中的单例对象,其内部实际上是一个`IWindowSession`类型,通过`AIDL`接口与系统进程中的`Session`对象进行通信,最终实现`DecorView`的添加和显示。

       通过`setView()`方法的实现,可以看到除了调用`IWindowSession`进行跨进程添加`View`之外,还会设置输入事件处理。当触屏事件发生时,这些事件首先通过驱动层的优化计算,通过`Socket`跨进程通知`Android Framework`层,最终触屏事件会通过输入管道传送到`DecorView`处理。

       在`DecorView`内部,触屏事件会通过`onProcess`方法传递给`mView`,即`PhoneWindow`中的`DecorView`。最终,事件传递到`PhoneWindow`中的`View.java`实现的`dispatchPointerEvent()`方法,并调用`Window.Callback`的`dispatchTouchEvent(ev)`方法。对于`Activity`来说,`dispatchTouchEvent()`方法最终还是会调用`PhoneWindow`的`superDispatchTouchEvent()`,然后传递给`DecorView`的`superDispatchTouchEvent()`方法,完成事件的分发和处理。

       综上所述,通过`setContentView()`的过程,我们可以清晰地看到`Activity`、`Window`、`View`之间的交互关系。整个过程主要由`PhoneWindow`组件主导,而`Activity`主要负责提供要显示的布局资源,其与屏幕的直接交互则通过`WindowManager`和`WindowManagerService`实现。

android开发设置屏蔽录制

       é¡¹ç›®å¼€å‘中,为了用户信息的安全,会有禁止页面被截屏、录屏的需求。

       è¿™ç±»èµ„料,在网上有很多,一般都是通过设置Activity的Flag解决,如:

       //禁止页面被截屏、录屏getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);

       è¿™ç§è®¾ç½®å¯è§£å†³ä¸€èˆ¬çš„防截屏、录屏的需求。

       å¦‚果页面中有弹出Popupwindow,在录屏视频中的效果是:

       éžPopupwindow区域为黑色

       ä½†Popupwindow区域仍然是可以看到的

       å¦‚下面两张Gif图所示:

       æœªè®¾ç½®FLAG_SECURE,录屏的效果,如下图(git图片中间的水印忽略):

       è®¾ç½®äº†FLAG_SECURE之后,录屏的效果,如下图(git图片中间的水印忽略):

       åŽŸå› åˆ†æž

       çœ‹åˆ°äº†ä¸Šé¢çš„效果,我们可能会有疑问PopupWindow不像Dialog有自己的window对象,而是使用WindowManager.addView方法将View显示在Activity窗体上的。那么,Activity已经设置了FLAG_SECURE,为什么录屏时还能看到PopupWindow?

       æˆ‘们先通过getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);来分析下源码:

       1、Window.java

       //window布局参数private final WindowManager.LayoutParams mWindowAttributes =        new WindowManager.LayoutParams();//添加标识public void addFlags(int flags) {

       setFlags(flags, flags);

       }//通过mWindowAttributes设置标识public void setFlags(int flags, int mask) {        final WindowManager.LayoutParams attrs = getAttributes();

       attrs.flags = (attrs.flags&~mask) | (flags&mask);

       mForcedWindowFlags |= mask;

       dispatchWindowAttributesChanged(attrs);

       }//获得布局参数对象,即mWindowAttributespublic final WindowManager.LayoutParams getAttributes() {        return mWindowAttributes;

       }

       é€šè¿‡æºç å¯ä»¥çœ‹åˆ°ï¼Œè®¾ç½®window属性的源码非常简单,即:通过window里的布局参数对象mWindowAttributes设置标识即可。

       2、PopupWindow.java

       //显示PopupWindowpublic void showAtLocation(View parent, int gravity, int x, int y) {

       mParentRootView = new WeakReference<>(parent.getRootView());

       showAtLocation(parent.getWindowToken(), gravity, x, y);

       }//显示PopupWindowpublic void showAtLocation(IBinder token, int gravity, int x, int y) {        if (isShowing() || mContentView == null) {            return;

       }

       TransitionManager.endTransitions(mDecorView);

       detachFromAnchor();

       mIsShowing = true;

       mIsDropdown = false;

       mGravity = gravity;

       //创建Window布局参数对象

       final WindowManager.LayoutParams p =createPopupLayoutParams(token);

       preparePopup(p);

       p.x = x;

       p.y = y;

       invokePopup(p);

       }//创建Window布局参数对象protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {        final WindowManager.LayoutParams p = new WindowManager.LayoutParams();

       p.gravity = computeGravity();

       p.flags = computeFlags(p.flags);

       p.type = mWindowLayoutType;

       p.token = token;

       p.softInputMode = mSoftInputMode;

       p.windowAnimations = computeAnimationResource();        if (mBackground != null) {

       p.format = mBackground.getOpacity();

       } else {

       p.format = PixelFormat.TRANSLUCENT;

       }        if (mHeightMode < 0) {

       p.height = mLastHeight = mHeightMode;

       } else {

       p.height = mLastHeight = mHeight;

       }        if (mWidthMode < 0) {

       p.width = mLastWidth = mWidthMode;

       } else {

       p.width = mLastWidth = mWidth;

       }

       p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH

       | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;

       p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));        return p;

       }//将PopupWindow添加到Window上private void invokePopup(WindowManager.LayoutParams p) {        if (mContext != null) {

       p.packageName = mContext.getPackageName();

       }        final PopupDecorView decorView = mDecorView;

       decorView.setFitsSystemWindows(mLayoutInsetDecor);

       setLayoutDirectionFromAnchor();

       mWindowManager.addView(decorView, p);        if (mEnterTransition != null) {

       decorView.requestEnterTransition(mEnterTransition);

       }

       }

       é€šè¿‡PopupWindow的源码分析,我们不难看出,在调用showAtLocation时,会单独创建一个WindowManager.LayoutParams布局参数对象,用于显示PopupWindow,而该布局参数对象上并未设置任何防止截屏Flag。

       å¦‚何解决

       åŽŸå› æ—¢ç„¶æ‰¾åˆ°äº†ï¼Œé‚£ä¹ˆå¦‚何处理呢?

       å†å›žå¤´åˆ†æžä¸‹Window的关键代码:

       //通过mWindowAttributes设置标识public void setFlags(int flags, int mask) {        final WindowManager.LayoutParams attrs = getAttributes();

       attrs.flags = (attrs.flags&~mask) | (flags&mask);

       mForcedWindowFlags |= mask;

       dispatchWindowAttributesChanged(attrs);

       }

       å…¶å®žåªéœ€è¦èŽ·å¾—WindowManager.LayoutParams对象,再设置上flag即可。

       ä½†æ˜¯PopupWindow并没有像Activity一样有直接获得window的方法,更别说设置Flag了。我们再分析下PopupWindow的源码:

       //将PopupWindow添加到Window上private void invokePopup(WindowManager.LayoutParams p) {        if (mContext != null) {

       p.packageName = mContext.getPackageName();

       }

       final PopupDecorView decorView = mDecorView;

       decorView.setFitsSystemWindows(mLayoutInsetDecor);

       setLayoutDirectionFromAnchor();        //添加View

       mWindowManager.addView(decorView, p);        if (mEnterTransition != null) {

       decorView.requestEnterTransition(mEnterTransition);

       }

       }

       æˆ‘们调用showAtLocation,最终都会执行mWindowManager.addView(decorView, p);

       é‚£ä¹ˆæ˜¯å¦å¯ä»¥åœ¨addView之前获取到WindowManager.LayoutParams呢?

       ç­”案很明显,默认是不可以的。因为PopupWindow并没有公开获取WindowManager.LayoutParams的方法,而且mWindowManager也是私有的。

       å¦‚何才能解决呢?

       æˆ‘们可以通过hook的方式解决这个问题。我们先使用动态代理拦截PopupWindow类的addView方法,拿到WindowManager.LayoutParams对象,设置对应Flag,再反射获得mWindowManager对象去执行addView方法。

       é£Žé™©åˆ†æžï¼š

       ä¸è¿‡ï¼Œé€šè¿‡hook的方式也有一定的风险,因为mWindowManager是私有对象,不像Public的API,谷歌后续升级Android版本不会考虑其兼容性,所以有可能后续Android版本中改了其名称,那么我们通过反射获得mWindowManager对象不就有问题了。不过从历代版本的Android源码去看,mWindowManager被改的几率不大,所以hook也是可以用的,我们尽量写代码时考虑上这种风险,避免以后出问题。

       public class PopupWindow {

       ......    private WindowManager mWindowManager;

       ......

       }

       è€ŒaddView方法是ViewManger接口的公共方法,我们可以放心使用。

       public interface ViewManager{    public void addView(View view, ViewGroup.LayoutParams params);    public void updateViewLayout(View view, ViewGroup.LayoutParams params);    public void removeView(View view);

       }

       åŠŸèƒ½å®žçŽ°

       è€ƒè™‘到hook的可维护性和扩展性,我们将相关代码封装成一个独立的工具类吧。

       package com.ccc.ddd.testpopupwindow.utils;

       import android.os.Handler;

       import android.view.WindowManager;

       import android.widget.PopupWindow;

       import java.lang.reflect.Field;

       import java.lang.reflect.InvocationHandler;

       import java.lang.reflect.Method;

       import java.lang.reflect.Proxy;public class PopNoRecordProxy implements InvocationHandler {    private Object mWindowManager;//PopupWindow类的mWindowManager对象

       public static PopNoRecordProxy instance() {        return new PopNoRecordProxy();

       }    public void noScreenRecord(PopupWindow popupWindow) {        if (popupWindow == null) {            return;

       }        try {            //通过反射获得PopupWindow类的私有对象:mWindowManager

       Field windowManagerField = PopupWindow.class.getDeclaredField("mWindowManager");

       windowManagerField.setAccessible(true);

       mWindowManager = windowManagerField.get(popupWindow);            if(mWindowManager == null){                return;

       }            //创建WindowManager的动态代理对象proxy

       Object proxy = Proxy.newProxyInstance(Handler.class.getClassLoader(), new Class[]{ WindowManager.class}, this);            //注入动态代理对象proxy(即:mWindowManager对象由proxy对象来代理)

       windowManagerField.set(popupWindow, proxy);

       } catch (IllegalAccessException e) {

       e.printStackTrace();

       } catch (NoSuchFieldException e) {

       e.printStackTrace();

       }

       }

       @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        try {            //拦截方法mWindowManager.addView(View view, ViewGroup.LayoutParams params);

       if (method != null && method.getName() != null && method.getName().equals("addView")

       && args != null && args.length == 2) {                //获取WindowManager.LayoutParams,即:ViewGroup.LayoutParams

       WindowManager.LayoutParams params = (WindowManager.LayoutParams) args[1];                //禁止录屏

       setNoScreenRecord(params);

       }

       } catch (Exception ex) {

       ex.printStackTrace();

       }        return method.invoke(mWindowManager, args);

       }    /

**

       * 禁止录屏

       */

       private void setNoScreenRecord(WindowManager.LayoutParams params) {

       setFlags(params, WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);

       }    /

**

       * 允许录屏

       */

       private void setAllowScreenRecord(WindowManager.LayoutParams params) {

       setFlags(params, 0, WindowManager.LayoutParams.FLAG_SECURE);

       }    /

**

       * 设置WindowManager.LayoutParams flag属性(参考系统类Window.setFlags(int flags, int mask))

       

*

       * @param params WindowManager.LayoutParams

       * @param flags  The new window flags (see WindowManager.LayoutParams).

       * @param mask   Which of the window flag bits to modify.

       */

       private void setFlags(WindowManager.LayoutParams params, int flags, int mask) {        try {            if (params == null) {                return;

       }            params.flags = (params.flags & ~mask) | (flags & mask);

       } catch (Exception ex) {

       ex.printStackTrace();

       }

       }

       }

       Popwindow禁止录屏工具类的使用,代码示例:

          //创建PopupWindow

       //正常项目中,该方法可改成工厂类

       //正常项目中,也可自定义PopupWindow,在其类中设置禁止录屏

       private PopupWindow createPopupWindow(View view, int width, int height) {

       PopupWindow popupWindow = new PopupWindow(view, width, height);        //PopupWindow禁止录屏

       PopNoRecordProxy.instance().noScreenRecord(popupWindow);        return popupWindow;

       }   //显示Popupwindow

       private void showPm() {

       View view = LayoutInflater.from(this).inflate(R.layout.pm1, null);

       PopupWindow  pw = createPopupWindow(view,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

       pw1.setFocusable(false);

       pw1.showAtLocation(this.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.RIGHT, PopConst.PopOffsetX, PopConst.PopOffsetY);

       }

       å½•å±æ•ˆæžœå›¾ï¼š

手机直播源码,设置透明背景(去掉蒙层)状态栏颜色不改变

       手机直播源码中,若要设置透明背景(去掉蒙层)并确保状态栏颜色不改变,可以遵循以下步骤。

       具体来说,对Dialog进行设置时,通过调用addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)来添加灰色蒙层,此时状态栏颜色会变为亮色。要解决这个问题,需调用clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)来移除蒙层,确保状态栏颜色保持不变。

       完整代码实现如下,确保在Dialog初始化阶段,调用clearFlags方法移除蒙层属性。这样设置透明背景的同时,状态栏颜色将不因Dialog弹出而改变。

       遵循以上方法,可实现手机直播源码中Dialog组件的透明背景设置与状态栏颜色的保持一致,无需额外关注后续文章以获取更多内容。

copyright © 2016 powered by 皮皮网   sitemap