Android:Window相关理解

这篇具有很好参考价值的文章主要介绍了Android:Window相关理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、Window概述

Window概念

  • window是一个抽象类,主要用来处理窗口的展示与行为策略(比如触摸,点击等)。
  • window是View的直接管理者
  • window类的实例应该是作为顶级view,被添加到windowManager的顶级视图。(PhoneWindow)
  • window提供了标准的UI策略,如背景,标题区域,默认密钥处理等。
  • widnow唯一的实现类是android.view.PhoneWindow,如果要使用window就必须通过android.view.PhoneWindow。

Window和DecorView

  • 一个Activity对应一个PhoneWindow,PhoneWidnow会处理这个activity中的ui展示和 用户的行为(如触摸,点击等)。
  • PhoneWidnow不是一个View对象,通过将PhoneWindow添加到windowManager中,PhoneWindow能够将要处理的行为事件传递给DecorView。
  • DecorView继承自FrameLayout,是除了Window之外最顶级的视图。
  • ContentView就是我们通常使用activity.setContentView()中设置的View。它所对应的id是R.id.content。
    Android:Window相关理解

二、Window属性和类型

添加窗口是通过WindowManagerGlobal的addView方法操作的,这里有三个必要参数。view,params,display。 display : 表示要输出的显示设备。 view : 表示要显示的View,一般是对该view的上下文进行操作。(view.getContext()) params : 类型为WindowManager.LayoutParams,即表示该View要展示在窗口上的布局参数。其中有一个重要的参数type,用来表示窗口的类型。

Window的类型

应用窗口

Activity 对应的窗口类型是应用窗口, 所有 Activity 默认的窗口类型是 TYPE_BASE_APPLICATION。 WindowManager 的 LayoutParams 的默认类型是 TYPE_APPLICATION。 Dialog 并没有设置type,所以也是默认的窗口类型即 TYPE_APPLICATION

子窗口

子窗口不能单独存在,它需要附属在特定的父Window之中,常见的存在便是PopWindow,之所以称其为子窗口,是因为其的存在或出现依附于父窗口,父窗口显现子窗口才能出现,反之亦然

系统窗口

系统窗口跟应用窗口不同,不需要对应 Activity。跟子窗口不同,不需要有父窗口。一般来讲,系统窗口应该由系统来创建的,常见的系统窗口有音量调节栏、Toast弹窗和系统状态栏,屏保等。

Window的属性

这里介绍日常开发中我们或多或少会用到的Window属性

type参数
  • type参数,表示Window是什么类型,同时起到Z-order参数的作用,表示Window的层级,一般从0~9999,数值越大,越容易覆盖在屏幕上方

应用窗口的type属性值
应用程序Window的type值范围是[1-99],什么是应用程序Window,比如Activity所展示的页面,在WindowManager#LayoutParams中定义了如下应用程序的type值

// 应用程序 Window 的开始值\
public static final int FIRST_APPLICATION_WINDOW = 1;
// 应用程序 Window 的基础值\
public static final int TYPE_BASE_APPLICATION   = 1;\
// 普通的应用程序\
public static final int TYPE_APPLICATION        = 2;\
// 特殊的应用程序窗口,当程序可以显示 Window 之前使用这个 Window 来显示一些东西\
public static final int TYPE_APPLICATION_STARTING = 3;\
// TYPE_APPLICATION 的变体,在应用程序显示之前,WindowManager 会等待这个 Window 绘制完毕\
public static final int TYPE_DRAWN_APPLICATION = 4;\
// 应用程序 Window 的结束值\
public static final int LAST_APPLICATION_WINDOW = 99;

子窗口的type属性值
表示子Window,它的范围是[1000,1999],这些Window会按照Z-Order顺序依附于父Window上,而且他们的坐标是相当于父Window的,例如PopupWindow和一些Dialo

/**
 * 子Window的开始值,该Window的token必须设置在他们依附的父Window
 */
public static final int FIRST_SUB_WINDOW = 1000;

/**
 * 应用程序Window上面的面板
 */
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

/**
 * 用于显示多媒体(比如视频)的Window,这些Windows会显示在他们依附的Window后面
 */
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

/**
 * 应用程序Window上面的子面板
 */
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

/** 
 * 当前Window的布局和顶级Window布局相同时,不能作为子代的容器
 */
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

/**
 * 用于在媒体Window上显示覆盖物
 * @hide
 */
@UnsupportedAppUsage
public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

/**
 * 依附在应用Window上和它的子面板Window上的子面板
 * @hide
 */
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

/**
 * 子Window的结束值
 */
public static final int LAST_SUB_WINDOW = 1999;

系统窗口的属性值
系统Window的范围是[2000,2999],常见的系统的Window有Toast、输入法窗口、系统音量条窗口、系统错误窗口等,对应type的值如下

// 系统Window类型的开始值\
public static final int FIRST_SYSTEM_WINDOW     = 2000;\
\
// 系统状态栏,只能有一个状态栏,它被放置在屏幕的顶部,所有其他窗口都向下移动\
public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;\
\
// 系统搜索窗口,只能有一个搜索栏,它被放置在屏幕的顶部\
public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;\
\
// 已经从系统中被移除,可以使用 TYPE_KEYGUARD_DIALOG 代替\
public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;\
\
// 系统对话框窗口\
public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;\
\
// 锁屏时显示的对话框\
public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;\
\
@Deprecated\
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替\
public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;\
\
// 输入法窗口,位于普通 UI 之上,应用程序可重新布局以免被此窗口覆盖\
public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;\
\
// 输入法对话框,显示于当前输入法窗口之上\
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;\
\
// 墙纸\
public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;\
\
// 状态栏的滑动面板\
public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;\
\
// 应用程序叠加窗口显示在所有窗口之上\
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;\
\
// 系统Window类型的结束值\
public static final int LAST_SYSTEM_WINDOW      = 2999;

需要注意的是,使用系统窗口需要申请相应权限Manifest.permission.SYSTEM_ALERT_WINDOW

Flag属性

除了Type属性以外,Flag属性用以控制Window的一些行为特征,如出现时Window后方是否会变暗

// 当 Window 可见时允许锁屏\
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;\
\
// Window 后面的内容都变暗\
public static final int FLAG_DIM_BEHIND        = 0x00000002;\
\
@Deprecated\
// API 已经过时,Window 后面的内容都变模糊\
public static final int FLAG_BLUR_BEHIND        = 0x00000004;\
\
// Window 不能获得输入焦点,即不接受任何按键或按钮事件,例如该 Window 上 有 EditView,点击 EditView 是 不会弹出软键盘的\
// Window 范围外的事件依旧为原窗口处理;例如点击该窗口外的view,依然会有响应。另外只要设置了此Flag,都将会启用FLAG_NOT_TOUCH_MODAL\
public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;\
\
// 设置了该 Flag,将 Window 之外的按键事件发送给后面的 Window 处理, 而自己只会处理 Window 区域内的触摸事件\
// Window 之外的 view 也是可以响应 touch 事件。\
public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;\
\
// 设置了该Flag,表示该 Window 将不会接受任何 touch 事件,例如点击该 Window 不会有响应,只会传给下面有聚焦的窗口。\
public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;\
\
// 只要 Window 可见时屏幕就会一直亮着\
public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;\
\
// 允许 Window 占满整个屏幕\
public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;\
\
// 允许 Window 超过屏幕之外\
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;\
\
// 全屏显示,隐藏所有的 Window 装饰,比如在游戏、播放器中的全屏显示\
public static final int FLAG_FULLSCREEN      = 0x00000400;\
\
// 表示比FLAG_FULLSCREEN低一级,会显示状态栏\
public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;\
\
// 当用户的脸贴近屏幕时(比如打电话),不会去响应此事件\
public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;\
\
// 则当按键动作发生在 Window 之外时,将接收到一个MotionEvent.ACTION_OUTSIDE事件。\
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;\
\
@Deprecated\
// 窗口可以在锁屏的 Window 之上显示, 使用 Activity#setShowWhenLocked(boolean) 方法代替\
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;\
\
// 表示负责绘制系统栏背景。如果设置,系统栏将以透明背景绘制,\
// 此 Window 中的相应区域将填充 Window#getStatusBarColor()和 Window#getNavigationBarColor()中指定的颜色。\
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;\
\
// 表示要求系统壁纸显示在该 Window 后面,Window 表面必须是半透明的,才能真正看到它背后的壁纸\
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
软硬键盘

表示Window软键盘输入区域的显示模式,比如我们在微信聊天时,我们希望点击输入框软键盘弹起来的时候,能把输入框也顶上去,这样就可以看见自己输入的内容了。

// 没有指定状态,系统会选择一个合适的状态或者依赖于主题的配置
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;

// 当用户进入该窗口时,隐藏软键盘
public static final int SOFT_INPUT_STATE_HIDDEN = 2;

// 当窗口获取焦点时,隐藏软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;

// 当用户进入窗口时,显示软键盘
public static final int SOFT_INPUT_STATE_VISIBLE = 4;

// 当窗口获取焦点时,显示软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;

// window会调整大小以适应软键盘窗口
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;

// 没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;

// 当软键盘弹出时,窗口会调整大小,例如点击一个EditView,整个layout都将平移可见且处于软件盘的上方
// 同样的该模式不能与SOFT_INPUT_ADJUST_PAN结合使用;
// 如果窗口的布局参数标志包含FLAG_FULLSCREEN,则将忽略这个值,窗口不会调整大小,但会保持全屏。
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;

// 当软键盘弹出时,窗口不需要调整大小, 要确保输入焦点是可见的,
// 例如有两个EditView的输入框,一个为Ev1,一个为Ev2,当你点击Ev1想要输入数据时,当前的Ev1的输入框会移到软键盘上方
// 该模式不能与SOFT_INPUT_ADJUST_RESIZE结合使用
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;

// 将不会调整大小,直接覆盖在window上
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;
其他参数
  • x与y属性:指定Window左上角的位置。
  • alpha:Window的透明度。
  • gravity:Window在屏幕中的位置,使用的是Gravity类的常量。
  • format:Window的像素格式,值定义在PixelFormat中。

三、WindowManager.addView()

下面我们了解Window的添加过程,也就是WindowManager的addView()过程

我们先来看Window的内部机制图
Android:Window相关理解
对相关机制涉及到的类进行一番解读

  • ViewManager是个接口,用来规定View的一些控制行为
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 ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
  • WindowManagerImpl是WindowManager的代理类,可以通过getSystemService获取,来看WindowManagerImpl中的addView()
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
	applyDefaultToken(params);
	mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
  • 这里调用mGlobal.addView()去进行View的添加控制,需要注意的是,WindowManagerGlobal是一个全局单例类,实际上就是使用桥接模式将所有View的控制委托给WindowManagerGlobal进行控制
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {
    //判断参数的合法性    
    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;
    //如果是子Window,需要对参数做额外调整
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        final Context context = view.getContext();
        if (context != null
                && (context.getApplicationInfo().flags
                        & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }

    //ViewRootImpl实例
    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        // 省略
        //创建ViewRootImpl实例,并且设置参数
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        //分别记录View树 ViewRootImpl和Window参数,见分析1
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        
        try {
            //最后通过ViewRootImpl来添加Window,见分析2
            root.setView(view, wparams, panelParentView, userId);
        } catch (RuntimeException e) {
           ...
        }
    }
}
  • 由于WindowManagerGlobal是单例,它是真正WindowManager的逻辑实现类,所以需要把要处理的Window等都记录起来,这里使用了几个数据结构进行控制,注意到ViewRootImpl和Window其实是对应的关系,最后通过ViewRootImpl.setView()方法控制View的添加
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
  • 调用ViewRootImpl的setView()方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
            ...
            //分析1,在被添加到WindowManager之前调用一次
            requestLayout();
            ...
            //通过WindowSession来完成IPC调用,完成创建Window
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                adjustLayoutParamsForCompatibility(mWindowAttributes);
                controlInsetsForCompatibility(mWindowAttributes);
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                        mTempControls);
                if (mTranslator != null) {
                    mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
                    mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
                }
            } catch (RemoteException e) {
                mAdded = false;
                mView = null;
                mAttachInfo.mRootView = null;
                mFallbackEventHandler.setView(null);
                unscheduleTraversals();
                setAccessibilityFocus(null, null);
                throw new RuntimeException("Adding window failed", e);
            } finally {
                if (restore) {
                    attrs.restore();
                }
            }

          ...
    }
}
  • 上面代码是ViewRootImpl的setView方法部分逻辑,它主要干俩件事,第一件事就是更新界面,在注释分析1的地方,通过调用requestLayout来完成异步刷新请求,方法实现如下
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
  • 其中scheduleTraversal方法就是View绘制的入口,接着会通过mWindowSession的addToDisplay方法来完成Window的添加过程,那这个mWindowSession是什么类的实例呢,通过查看源码可知,mWindowSession是一个IWindowSession对象,而IWindowSession是一个IBinder接口,所以mWindowSession只是一个Binder对象,而实现类在WindowManagerService中,这里通过mWindowSession完成了IPC通信。
    然后真正添加Window的逻辑就交由WindowManagerService(简称WMS)了,由于WMS比较复杂,这里就不过多深入了。

ViewRootImpl在其中起到的作用就是View和WindowManagerService的桥梁,在该类中对View进行了绘制,同时又通过IPC通信让WMS创建了Window

对于其中几个重要的类,进行梳理如下

  • ViewRootImpl,在调用addView时会创建实例,这也就说明一个View树对应一个ViewRootImpl,同时它是Window和View之间的桥梁,一边负责View的绘制,一边负责IPC通信到WMS创建Window。

  • IWindowSession实例,它是APP范围内单例,是一个Binder,负责和WMS通信。这里为什么一个一个应用就一个实例呢,这是因为WMS是系统服务,它要服务很多个APP,而一个APP又有多个Window,所以每个Window都要WMS来管理,则太多了,这样WMS只需要和APP的IWindowSession进行通信即可。

  • WindowManagerGlobal实例,前面我们调用WindowManager的addView方法时,会调用该类的单例,它可以看成是WindowManager的实现单例。

关于IWindowSession通信过程如下
Android:Window相关理解

总结

以上便是笔者关于Window的相关理解文章来源地址https://www.toymoban.com/news/detail-439877.html

到了这里,关于Android:Window相关理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android窗口层级(Window Type)分析

    Android的窗口Window分为三种类型: 应用Window,比如 Activity 、 Dialog ;子Window,比如 PopupWindow ;系统Window,比如 Toast 、系统状态栏、导航栏等等。 应用Window的Z-Ordered最低,就是在系统中的显示层级最低,然后到子Window,层级最高的是系统Window。层级高的Window会覆盖层级低的W

    2024年02月09日
    浏览(41)
  • 【Android】WMS(二)Window的添加

    在 Android 应用开发中,软键盘的显示与隐藏是一个经常出现的问题,而 WindowManager 的 LayoutParams 中定义的软键盘相关模式则为开发者提供了一些解决方案。 其中, SoftInputMode 就是用于描述软键盘的显示方式和窗口的调整方式的属性。常用的几个 SoftInputMode 的作用: SOFT_INPUT_

    2024年02月09日
    浏览(53)
  • 【Android】WMS(三)Window的更新&UI刷新

    在 Android 中,窗口的更新是一个非常常见的事情。比如,在使用 App 过程中,需要弹出键盘窗口或者切换横竖屏时,就会发生窗口的更新。 首先,当需要更新窗口时,会调用 WindowManager 的 updateViewLayout() 方法来设置参数,并将参数设置到 对应的 View 上。WindowManager 的实现类为

    2024年02月09日
    浏览(77)
  • (增加细粒度资源管理)深入理解flink的task slot相关概念

    之前对flink的task slot的理解太浅了,重新捋一下相关知识点 我们知道,flink中每个TaskManager都是一个 JVM 进程,可以在单独的线程中执行一个或多个 subtask(线程)。 但是TaskManager 的计算资源是有限的,并不是所有任务都可以放在同一个 TaskManager 上并行执行。并行的任务越多

    2024年03月11日
    浏览(41)
  • 【window10】Dart+Android Studio+Flutter安装及运行

    安装前,请配置好你的jdk环境,准备好你的梯子~ 浅浅了解一下Dart: Dart 诞生于2011年,是由谷歌开发的一种强类型、跨平台的客户端开发语言。Dart是一种简洁、清晰、基于类的面向对象的语言,具有专门为客户端优化、高生产力、快速高效、可移植(兼容ARM/x86)、易学的

    2024年02月06日
    浏览(52)
  • 深入理解flinksql执行流程,calcite与catalog相关概念,扩展解析器实现语法的扩展

    flink在执行sql语句时,是无法像java/scala代码一样直接去使用的,需要解析成电脑可以执行的语言,对sql语句进行解析转化。 这里说的我感觉其实不是特别准确,应该是 flink使用的是一款开源SQL解析工具Apache Calcite ,Calcite使用Java CC对sql语句进行了解析 。 那么我们先来简单说

    2024年02月21日
    浏览(46)
  • Android dp px ppi pt等概念的理解

    做Android开发过程中,总会用到px、dp、pt等概念,下面对它们代表的意义以及互相之间的关系做简单的介绍。 1、px 2、ppi 3、pt 4、dp 5、Android获取屏幕状态信息 就是一个颜色点,一个像素点,最小的单位。不同的颜色点组成一张图像,因此图像的基本单位是像素px。它是一个相

    2024年02月06日
    浏览(45)
  • (TinkSystem SR650)安装服务器操作系统(Windows Server 2022)步骤和相关概念

    服务器操作系统安装步骤 记录一下服务器操作系统安装过程,虽然简单但还是有一些坑需要注意,本次使用的是联想服务器ThinkSystem SR650;安装windows Server 2022 Standard(桌面)。 下载windows server 2022镜像文件(.iso)并且解压出来(不解压安装的时候服务器不能找到镜像文件)

    2024年02月13日
    浏览(42)
  • 解决:Unable to add window -- token android.os.BinderProxy is not valid; is your activity running?

    这个问题是我想存储已经创建过的弹窗进行复用过程当中出现的报错,具体代码请看: 在查看报错之后可以确定是 Context 上下文的问题。 在创建Dialog时需要在构造方法当中传递一个Context,如果这个上下文被销毁即这个Activity已经走了OnDestroy方法。那么这个上下文就无效了,

    2024年02月09日
    浏览(52)
  • android framework实战开发之WINDOWING_MODE_FREEFORM自由窗口相关

    hi,粉丝朋友们! 今天开始就进入正式的自由窗口的相关的内容讲解,blog只是一些知识点的记录,更多的干货,还请看马哥的视频,及视频配套资料。 b站免费视频教程讲解: https://www.bilibili.com/video/BV1wj411o7A9/ aosp默认并没有公开自由窗口模式,如果需要体验自由窗口模式必须

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包