欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > Android:如何绘制View

Android:如何绘制View

2024/10/24 18:21:04 来源:https://blog.csdn.net/ChinaDragon10/article/details/140253670  浏览:    关键词:Android:如何绘制View

点击查看Android 如何绘制视图官网

一、简介

Android 框架会在 Activity 获得焦点时请求 Activity 绘制其布局。Android 框架会处理绘制流程,但该 Activity 必须提供其布局层次结构的根节点。

Android 框架会绘制布局的根节点,并测量和绘制布局树。它会通过遍历布局树并渲染与无效区域相交的每个 View 进行绘制。每个 ViewGroup 负责请求绘制其每个子级(使用 draw() 方法),而每个 View 负责绘制其本身。由于布局树已经过系统预先遍历,因此该框架会在子级之前(即后方)绘制父级,而其同级会按照它们在布局树中出现的顺序进行绘制。

注意:该框架不会绘制有效区域之外的 View 对象,也不会负责为您绘制 View 背景。您可以通过调用 invalidate() 来强制绘制 View。

Android 框架绘制布局包含两个遍历流程一个测量遍历和一个布局遍历。该框架会在 measure(int, int) 中执行测量遍历,并执行 View 树的自上而下遍历。在递归过程中,每个 View 都会将维度规范下推到布局树。测量遍历结束时,每个 View 都会存储其测量值。该框架会在 layout(int, int, int, int) 中执行第二次遍历,也是自上而下遍历。在此次遍历中,每个父级负责使用测量遍历中计算的尺寸来定位其所有的子级。

以下各部分更详细地介绍了布局流程的两个遍历。

二、启动测量遍历

当返回 View 对象的 measure() 方法时,请设置其 getMeasuredWidth() 和 getMeasuredHeight() 值,以及 View 对象的所有后代的值。View 对象的测量宽度值和测量高度值必须遵守 View 对象的父级所施加的限制。这有助于保证在测量遍历结束时,所有父级都会接受其子级的所有测量值。

父级 View 可以对其子级多次调用 measure()。例如,父级可以使用未指定的维度测量子级一次,以确定它们的首选尺寸。如果子级的不受限尺寸的总和过大或过小,则父级可以使用限制子级尺寸的值再次调用 measure()。

测量遍历使用两个类来传达维度。View 对象使用 ViewGroup.LayoutParams 来传达其首选尺寸和位置。基本 ViewGroup.LayoutParams 类描述了 View 的首选宽度和高度。针对每个维度,它可以指定以下某一项:

  • 一个确切的尺寸。
  • MATCH_PARENT,此参数意味着 View 的首选尺寸是其父级的尺寸(负填充)。
  • WRAP_CONTENT,此参数意味着 View 的首选尺寸恰好足以容纳其内容(正填充)。

有适用于 ViewGroup 的不同子类的 ViewGroup.LayoutParams 子类。例如,RelativeLayout 有自己的 ViewGroup.LayoutParams 子类,其中包括可使子级 View 对象水平居中和垂直居中的功能。

MeasureSpec 对象用于在树中将要求从父级下推到子级。MeasureSpec 可以是下述三种模式之一:

  • UNSPECIFIED:父级使用此模式来确定子级 View 的目标维度。例如,LinearLayout 可对高度设置为 UNSPECIFIED 且宽度设置为 EXACTLY 240 的子级调用 measure(),以确定给定宽度为 240 像素的子级 View 所需的高度。
  • EXACTLY:父级使用此模式来强制子级使用某个确切尺寸。子级必须使用此尺寸,并保证其所有的后代都能放入此尺寸。
  • AT MOST:父级使用此模式来强制规定子级的最大尺寸。子级必须保证它及其所有的后代都能放入此尺寸。

三、启动布局遍历

如需启动布局,请调用 requestLayout()。当 View 认为自己无法再放入当前范围时,通常会调用此方法。

四、实现自定义测量和布局逻辑

如果您要实现自定义测量或布局逻辑,请替换实现该逻辑的方法:onMeasure(int, int) 和 onLayout(boolean, int, int, int, int)。 这些方法分别由 measure(int, int) 和 layout(int, int, int, int) 调用。请勿尝试替换 measure(int, int) 或 layout(int, int) 方法 - 这两种方法都是 final,因此无法被替换。

以下示例展示了如何在 WindowManager 示例应用的“SplitLayout”类中执行此操作。如果 SplitLayout 具有两个或更多子视图,并且显示屏具有折叠边,则它会将两个子视图放置在折叠边的任一侧。以下示例展示了用于替换测量和布局的用例,但对于生产环境,请使用 SlidingPaneLayout(如果您希望出现此行为)。

/*** An example of split-layout for two views, separated by a display* feature that goes across the window. When both start and end views are* added, it checks whether there are display features that separate the area* in two—such as a fold or hinge—and places them side-by-side or* top-bottom.*/
class SplitLayout : FrameLayout {private var windowLayoutInfo: WindowLayoutInfo? = nullprivate var startViewId = 0private var endViewId = 0private var lastWidthMeasureSpec: Int = 0private var lastHeightMeasureSpec: Int = 0...fun updateWindowLayout(windowLayoutInfo: WindowLayoutInfo) {this.windowLayoutInfo = windowLayoutInforequestLayout()}override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {val startView = findStartView()val endView = findEndView()val splitPositions = splitViewPositions(startView, endView)if (startView != null && endView != null && splitPositions != null) {val startPosition = splitPositions[0]val startWidthSpec = MeasureSpec.makeMeasureSpec(startPosition.width(), EXACTLY)val startHeightSpec = MeasureSpec.makeMeasureSpec(startPosition.height(), EXACTLY)startView.measure(startWidthSpec, startHeightSpec)startView.layout(startPosition.left, startPosition.top, startPosition.right,startPosition.bottom)val endPosition = splitPositions[1]val endWidthSpec = MeasureSpec.makeMeasureSpec(endPosition.width(), EXACTLY)val endHeightSpec = MeasureSpec.makeMeasureSpec(endPosition.height(), EXACTLY)endView.measure(endWidthSpec, endHeightSpec)endView.layout(endPosition.left, endPosition.top, endPosition.right,endPosition.bottom)} else {super.onLayout(changed, left, top, right, bottom)}}/*** Gets the position of the split for this view.* @return A rect that defines of split, or {@code null} if there is no split.*/private fun splitViewPositions(startView: View?, endView: View?): Array

在这里插入图片描述

五、SlidingPaneLayout

5.1 创建双窗格布局

SlidingPaneLayout 组件支持在较大的设备和可折叠设备上并排显示两个窗格,同时自动进行调整,以便在手机等较小的设备上一次仅显示一个窗格。

5.2 添加依赖

如需使用 SlidingPaneLayout,请在应用的 build.gradle 文件中添加以下依赖项:

dependencies {implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
}
5.4 XML 布局配置

SlidingPaneLayout 提供一种水平的双窗格布局,可在界面的顶层使用。在这种布局中,第一个窗格用作内容列表或浏览器,从属于另一个窗格中用于显示内容的主要详细信息视图。

在这里插入图片描述

六、SlidingPaneLayout 示例

点击查查GitHub 上的 SlidingPaneLayout 示例

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com