Java 中文官方教程 2022 版(三十一)(1)https://developer.aliyun.com/article/1487974
椭圆
Ellipse2D
类表示由边界矩形定义的椭圆。Ellipse2D.Float
和Ellipse2D.Double
子类指定了以浮点和双精度表示的椭圆。
椭圆由位置、宽度和高度完全定义。例如:
// draw Ellipse2D.Double g2.draw(new Ellipse2D.Double(x, y, rectwidth, rectheight));
弧
要绘制椭圆的一部分,您可以使用Arc2D
类。这个类表示由边界矩形、起始角度、角度范围和闭合类型定义的弧。Arc2D.Float
和Arc2D.Double
子类指定了以浮点和双精度表示的弧。
Arc2D
类定义了这个类中对应常量表示的三种弧形:OPEN、PIE 和 CHORD。
有几种方法可以设置弧的大小和参数:
- 直接,通过坐标
- 通过提供的
Point2D
和Dimension2D
- 通过复制现有的
Arc2D
此外,您可以使用setArcByCenter
方法来指定从中心点开始的弧,给定其坐标和半径。
// draw Arc2D.Double g2.draw(new Arc2D.Double(x, y, rectwidth, rectheight, 90, 135, Arc2D.OPEN));
ShapesDemo2D.java
代码示例包含了所有描述的几何原语的实现。有关本节中所代表的类和方法的更多信息,请参阅java.awt.geom
规范。
绘制任意形状
原文:
docs.oracle.com/javase/tutorial/2d/geometry/arbitrary.html
您已经学会了如何绘制java.awt.geom
包中表示的大多数形状。要创建更复杂的几何图形,如多边形、折线或星形,您可以使用此包中的另一个类GeneralPath
。
此类实现了Shape
接口,表示由线段、二次曲线和三次曲线构成的几何路径。此类中的三个构造函数可以使用默认绕组规则(WIND_NON_ZERO
)、给定的绕组规则(WIND_NON_ZERO
或WIND_EVEN_ODD
)或指定的初始坐标容量创建GeneralPath
对象。绕组规则指定了如何确定路径的内部。
public void paint (Graphics g) { Graphics2D g2 = (Graphics2D) g; ... }
要创建一个空的GeneralPath
实例,请调用new GeneralPath()
,然后使用以下方法向形状添加线段:
moveTo(float x, float y)
– 将路径的当前点移动到给定点lineTo(float x, float y)
– 向当前路径添加一个直线段quadTo(float ctrlx, float ctrly, float x2, floaty2)
– 向当前路径添加一个二次曲线段curveTo(float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float x3, floaty3)
– 向当前路径添加一个三次曲线段closePath()
– 关闭当前路径
以下示例说明了如何使用GeneralPath
绘制折线:
|
// draw GeneralPath (polyline) int x2Points[] = {0, 100, 0, 100}; int y2Points[] = {0, 50, 50, 0}; GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x2Points.length); polyline.moveTo (x2Points[0], y2Points[0]); for (int index = 1; index < x2Points.length; index++) { polyline.lineTo(x2Points[index], y2Points[index]); }; g2.draw(polyline);
此示例说明了如何使用GeneralPath
绘制多边形:
|
// draw GeneralPath (polygon) int x1Points[] = {0, 100, 0, 100}; int y1Points[] = {0, 50, 50, 0}; GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x1Points.length); polygon.moveTo(x1Points[0], y1Points[0]); for (int index = 1; index < x1Points.length; index++) { polygon.lineTo(x1Points[index], y1Points[index]); }; polygon.closePath(); g2.draw(polygon);
请注意,最后两个代码示例之间唯一的区别是closePath()
方法。此方法通过向上一次moveTo
的坐标绘制一条直线,从而将折线变成多边形。
要将特定路径添加到您的GeneralPath
对象的末尾,您可以使用append()
方法之一。ShapesDemo2D.java
代码示例包含了任意形状的额外实现。
描边和填充图形原语
原文:
docs.oracle.com/javase/tutorial/2d/geometry/strokeandfill.html
您已经知道如何创建不同的几何原语和更复杂的形状。本课程教授如何为图形添加一些颜色和花哨的轮廓,并表示填充和描边:
- 填充 - 是用纯色、颜色渐变或纹理图案绘制形状内部的过程
- 描边 - 是绘制形状轮廓的过程,应用描边宽度、线条样式和颜色属性
要将花哨的线条样式和填充图案应用于几何原语,需在呈现之前更改Graphics2D
上下文中的描边和绘制属性。例如,通过创建适当的Stroke
对象来绘制虚线。在呈现线条之前,将此描边添加到Graphics2D
上下文中,调用setStroke
方法。同样,通过创建GradientPaint
对象并将其添加到Graphics2D
上下文中,可以将渐变填充应用于Shape
对象。
以下代码行丰富了几何原语的填充和描边上下文:
// draw RoundRectangle2D.Double final static float dash1[] = {10.0f}; final static BasicStroke dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f); g2.setStroke(dashed); g2.draw(new RoundRectangle2D.Double(x, y, rectWidth, rectHeight, 10, 10));
// fill Ellipse2D.Double redtowhite = new GradientPaint(0,0,color.RED,100, 0,color.WHITE); g2.setPaint(redtowhite); g2.fill (new Ellipse2D.Double(0, 0, 100, 50));
ShapesDemo2D.java
代码示例代表了描边和填充的额外实现。
定义花哨的线条样式和填充图案
使用 Java 2D 的Stroke
和Paint
类,可以定义花哨的线条样式和填充图案。
线条样式
线条样式由Graphics2D
呈现上下文中的描边属性定义。要设置描边属性,需创建一个BasicStroke
对象并将其传递给Graphics2D
的setStroke
方法。
一个BasicStroke
对象保存有关线宽、连接样式、端点样式和虚线样式的信息。当使用draw
方法呈现Shape
时,将使用此信息。
线宽是线条垂直于其轨迹的厚度。线宽以用户坐标单位的float
值指定,当使用默认变换时,这些单位大致相当于 1/72 英寸。
连接样式是应用在两条线段相遇处的装饰。BasicStroke
支持以下三种连接样式:
JOIN_BEVEL
JOIN_MITER
JOIN_ROUND
端点样式是应用在线段结束处的装饰。BasicStroke
支持以下三种端点样式:
CAP_BUTT
CAP_ROUND
CAP_SQUARE
虚线样式定义了沿着线长度应用的不透明和透明部分的模式。虚线样式由一个虚线数组和一个虚线相位定义。虚线数组定义了虚线模式。数组中的交替元素表示用户坐标单位中的虚线长度和虚线之间的空间长度。元素 0 表示第一个虚线,元素 1 表示第一个空格,依此类推。虚线相位是虚线模式中的偏移量,也以用户坐标单位指定。虚线相位指示应用于线的开头的虚线模式的哪个部分。
填充图案
填充图案由Graphics2D
渲染上下文中的paint
属性定义。要设置paint
属性,您需要创建一个实现Paint
接口的对象实例,并将其传递给Graphics2D
的setPaint
方法。
以下三个类实现了Paint
接口:Color
、GradientPaint
和TexturePaint
。
要创建一个GradientPaint
,您需要指定起始位置和颜色以及结束位置和颜色。渐变沿着连接两个位置的线从一种颜色变化到另一种颜色。例如:
TexturePaint
类的图案由BufferedImage
类定义。要创建一个TexturePaint
对象,您需要指定包含图案的图像和用于复制和锚定图案的矩形。以下图像表示了这个特性:
课程:使用文本 APIs
本课程向您介绍了使用文本 API 的概念,以应用文本渲染功能。在本教程中,您已经使用了基本的 Java 2D 文本 API,并知道如何设置字体和位置,以及如何绘制文本。
本课程扩展了该材料,帮助您了解如何使用这些 API,并进一步了解 Java 2D 文本显示的功能。
这些主题将在以下部分中讨论。
物理和逻辑字体
这一部分解释了如何使用Font
类的方法来确定系统上可用的字体,创建Font
对象,并获取有关字体系列的信息。
测量文本
这一部分解释了如何通过使用FontMetrics
类的实例来正确测量文本。
高级文本显示
这一部分解释了如何定位和渲染一段样式文本,显示抗锯齿文本,使用文本属性来设置文本样式,并处理双向文本。
字体概念
原文:
docs.oracle.com/javase/tutorial/2d/text/fontconcepts.html
本节介绍了Font
类,支持详细字体信息的规范和复杂排版功能的使用。
一个Font
对象表示系统上可用的字体集合中的字体面实例。常见字体面的示例包括 Helvetica Bold 和 Courier Bold Italic。一个Font
对象关联三个名称:其逻辑名称、族名称和字体面名称:
- 一个
Font
对象的逻辑名称是映射到系统上可用的特定字体之一的名称。在 Java 中指定Font
时,请使用字体面名称而不是逻辑名称。您可以通过调用getName
方法从Font
中获取逻辑名称。要获取映射到系统上可用的特定字体的逻辑名称列表,请调用java.awt.GraphicsEnvironment.getAvailableFontFamilyNames方法。
查看物理和逻辑字体以获取更多信息。 - 一个
Font
对象的族名称是确定跨多个字体面的排版设计的字体族名称,如 Helvetica。通过getFamily
方法检索族名称。 - 一个
Font
对象的字体面名称指的是系统上安装的实际字体。这是您在指定字体时应该使用的名称。通常被称为字体名称。通过调用getFontName
检索字体名称。要确定系统上可用的字体面,请调用java.awt.GraphicsEnvironment.getAllFonts
方法。
您可以通过getAttributes
方法访问有关Font
的信息。Font
对象的属性包括其名称、大小、变换和字体特征,如粗细和姿势。
一个LineMetrics
对象封装了与Font
相关的测量信息,如其上升、下降和行间距:
- 上升是基线到上升线的距离。这个距离代表大写字母的典型高度,但有些字符可能会延伸到上升线以上。
- 下降是基线到下行线的距离。大多数字符的最低点将落在下降线内,但有些字符可能会延伸到下行线以下。
- 行间距是推荐的从下行线底部到下一行顶部的距离。
以下图显示了上升线、基线和下行线的位置:
这些信息用于正确定位字符沿着一行,以及相对于彼此定位行。您可以通过getAscent
、getDescent
和getLeading
方法访问这些行度量。您还可以通过LineMetrics
类访问有关Font
对象的高度、基线以及下划线和删除线特性的信息。
文本布局概念
原文:
docs.oracle.com/javase/tutorial/2d/text/textlayoutconcepts.html
在显示一段文本之前,必须使用适当的字形和连字对其进行正确的形状和定位。这个过程被称为文本布局。文本布局过程涉及以下内容:
- 使用适当的字形和连字来形状文本
- 正确排序文本
- 测量和定位文本
用于排列文本的信息也对执行文本操作(如插入符定位、点击检测和高亮显示)是必要的。查看处理双向文本以获取有关这些文本操作的更多信息。
要开发可以部署在国际市场的软件,文本必须以符合适当书写系统规则的方式排列在不同语言中。
本节涵盖以下主题:
- 形状文本
- 排序文本
- 测量和定位文本
形状文本
字形是一个或多个字符的视觉表示。字形的形状、大小和位置取决于其上下文。根据字体和样式,可以使用许多不同的字形来表示单个字符或字符组合。
例如,在手写草书文本中,特定字符的形状可能会因其与相邻字符的连接方式而异。
在某些书写系统中,特别是阿拉伯文中,必须始终考虑字形的上下文。与英语不同,阿拉伯文中的草书形式是强制性的;在阿拉伯文中,不能不使用草书形式呈现文本。
根据上下文,这些草书形式在形状上可能有很大差异。例如,阿拉伯字母heh有以下图中显示的四种草书形式:
尽管这四种形式彼此非常不同,但这种草书形式的变化与英语中的草书写作并无根本区别。
在某些情况下,两个字形的形状甚至可以发生更大变化,合并成一个单一的字形。这种合并的字形称为连字。例如,大多数英文字体包含以下图中显示的连字fi:
合并的字形考虑了字母f的悬挑,并以一种自然的方式组合字符,而不是简单地让字母相撞。
阿拉伯文中也使用连字,有些连字的使用是强制性的;在不使用适当的连字的情况下呈现某些字符组合是不可接受的。当从阿拉伯字符形成连字时,形状甚至比在英文中更根本地改变。例如,下图说明了当两个阿拉伯字符在一起时如何组合成单个连字。
文本排序
在 Java 编程语言中,文本使用 Unicode 字符编码进行编码。使用 Unicode 字符编码的文本以逻辑顺序存储在内存中。逻辑顺序是字符和单词被读取和写入的顺序。逻辑顺序不一定与视觉顺序相同,即对应字形显示的顺序。
特定书写系统(脚本)中字形的视觉顺序称为脚本顺序。例如,罗马文本的脚本顺序是从左到右,而阿拉伯文和希伯来文的脚本顺序是从右到左。
一些书写系统除了脚本顺序外,还有规则来排列文本行上的字形和单词。例如,阿拉伯文和希伯来文的数字是从左到右排列的,即使字母是从右到左排列的。这意味着阿拉伯文和希伯来文,即使没有嵌入英文文本,也是真正的双向文本。更多信息请参见处理双向文本。
测量和定位文本
除非您使用等宽字体,否则字体中的不同字符具有不同的宽度。这意味着所有文本的定位和测量都必须考虑到确切使用了哪些字符,而不仅仅是数量。例如,要在比例字体中右对齐显示的数字列,您不能简单地使用额外的空格来定位文本。为了正确对齐列,您需要知道每个数字的确切宽度,以便相应调整。
文本通常使用多种字体和样式显示,如粗体或斜体。在这种情况下,即使是相同的字符也可能有不同的形状和宽度,这取决于其样式。为了正确定位、测量和呈现文本,您需要跟踪每个单独的字符和应用于该字符的样式。幸运的是,TextLayout
类可以为您完成这些工作。
要正确显示希伯来文和阿拉伯文等语言的文本,需要测量和定位每个单独的字符,并将其放置在相邻字符的上下文中。由于字符的形状和位置可能会根据上下文而变化,因此在不考虑上下文的情况下测量和定位此类文本会产生不可接受的结果。
此外,Java SE 为您提供了FontMetrics
类,它使您能够获取由Font
对象渲染的文本的测量值,比如字体中一行文本的高度。您可以利用这些信息在 Java 图形应用程序中精确定位文本。更多信息请参见测量文本。
物理和逻辑字体
有两种字体:物理字体和逻辑字体。物理字体是实际的字体库,包括 TrueType 或 PostScript Type 1 字体。物理字体可以是 Time、Helvetica、Courier 或任何其他字体,包括国际字体。逻辑字体是以下五个字体系列:Serif、SansSerif、Monospaced、Dialog 和 DialogInput。这些逻辑字体不是实际的字体库。相反,Java 运行时环境通过逻辑字体名称将其映射到物理字体。
本节帮助您确定在应用程序中使用哪种字体。它涵盖以下主题:
- 物理字体
- Lucidia 字体
- 将物理字体与您的应用程序捆绑
- 逻辑字体
- 使用物理和逻辑字体的优缺点
- 字体配置文件
物理字体
物理字体是包含字形数据和表的实际字体库,使用 TrueType 或 PostScript Type 1 等字体技术,以将字符序列映射到字形序列。要获取系统中安装的所有可用字体系列的名称,请调用以下内容:
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String []fontFamilies = ge.getAvailableFontFamilyNames();
FontSelector 示例程序(在 FontSelector.java
中可用)演示了如何定位和选择这些字体。
注意: 应用程序不应假设任何特定的物理字体存在。然而,逻辑字体是一个安全的选择,因为它们始终存在。有关更多信息,请参阅逻辑字体。
注意: 如果您看不到 applet 运行,请至少安装Java SE Development Kit (JDK) 7 版本。
Lucidia 字体
Oracle 的 JRE 包含这一系列物理字体,也被许可用于其他 Java 平台的实现。这些字体是物理字体,但不依赖于主机操作系统。
使用这些字体的应用程序可以在这些字体可用的任何地方实现相同的外观。此外,这些字体涵盖了大量的语言(特别是欧洲和中东地区),因此您可以为支持的语言创建完全多语言的应用程序。然而,这些字体可能不在所有 JRE 中可用。此外,它们目前不涵盖完整的 Unicode 字符集;特别是,不支持中文、日文和韩文。
将物理字体与您的应用程序捆绑
有时,应用程序不能依赖于系统上安装的字体,通常是因为该字体是一种不可用的自定义字体。在这种情况下,必须将字体文件与应用程序捆绑在一起。
使用以下方法之一从现有物理字体创建 Font
对象:
Font java.awt.Font.createFont(int fontFormat, InputStream in); Font java.awt.Font.createFont(int fontFormat, File fontFile);
要从 TrueType 字体创建 Font
对象,形式参数 fontFormat
必须是常量 Font.TRUETYPE_FONT
。以下示例从 TrueType 字体文件 A.ttf
创建 Font
对象:
Font font = Font.createFont(Font.TRUETYPE_FONT, new File("A.ttf"));
直接从文件访问字体更简单、更方便。但是,如果您的代码无法访问文件系统资源,或者字体与应用程序或小程序的其余部分一起打包在 Java 存档(JAR)文件中,则可能需要一个 InputStream
对象。
createFont
方法创建一个具有点大小为 1 和样式 PLAIN
的新 Font
对象。然后可以使用 Font.deriveFont
方法将此基础字体用于派生具有不同大小、样式、变换和字体特性的新 Font
对象。例如:
try { //Returned font is of pt size 1 Font font = Font.createFont(Font.TRUETYPE_FONT, new File("A.ttf")); //Derive and return a 12 pt version: //Need to use float otherwise //it would be interpreted as style return font.deriveFont(12f); } catch (IOException|FontFormatException e) { // Handle exception }
使用 deriveFont
方法很重要,因为应用程序创建的字体不属于底层字体系统所知的字体集。由于 deriveFont
方法是从最初创建的字体工作的,因此它没有这种限制。
解决此问题的方法是将创建的字体注册到图形环境中。例如:
try { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File("A.ttf")); } catch (IOException|FontFormatException e) { //Handle exception }
在将字体注册到图形环境后,该字体可以在调用 getAvailableFontFamilyNames()
时使用,并且可以在字体构造函数中使用。
Java 中文官方教程 2022 版(三十一)(3)https://developer.aliyun.com/article/1487984