# 支持用户交互

> 原文： [https://docs.oracle.com/javase/tutorial/2d/advanced/user.html](https://docs.oracle.com/javase/tutorial/2d/advanced/user.html)

要使用户能够与您显示的图形进行交互，您需要能够确定用户何时单击其中一个图形。 [`Graphics2D`](https://docs.oracle.com/javase/8/docs/api/java/awt/Graphics2D.html) 类的`hit`方法提供了一种方法，可以轻松确定是否在特定 [`Shape`](https://docs.oracle.com/javase/8/docs/api/java/awt/Shape.html) 对象上发生鼠标点击。或者，您可以获取鼠标单击的位置并调用`Shape`上的`contains`以确定点击是否在`Shape`的范围内。

如果您使用的是原始文本，则可以通过获取与文本对应的大纲`Shape`然后使用`Shape`调用`hit`或`contains`来执行简单的命中测试。支持文本编辑需要更复杂的命中测试。如果要允许用户编辑文本，通常应使用其中一个 Swing 可编辑文本组件。如果您正在使用原始文本并使用 [`TextLayout`](https://docs.oracle.com/javase/8/docs/api/java/awt/font/TextLayout.html) 类来管理文本的整形和定位，您还可以使用`TextLayout`执行文本编辑的命中测试。有关更多信息，请参阅 [Java 2D 程序员指南](https://docs.oracle.com/javase/8/docs/technotes/guides/2d/spec/j2d-bookTOC.html)中的文本和字体一章，或参见下面的 HitTestSample 示例，它使用`TextLayout`执行简单的命中测试。

## 示例：ShapeMover

此 applet 允许用户在 applet 窗口中拖动`Shape`。在每个鼠标位置重绘`Shape`，以便在用户拖动时提供反馈。

&lt;applet alt="ShapeMover applet" archive="examples/lib/ShapeMoverApplet.jar" code="ShapeMover" height="250" width="550"&gt;&lt;param name="permissions" value="sandbox"&gt;&lt;/applet&gt;

* * *

**Note:**  If you don't see the applet running, you need to install at least the [Java SE Development Kit (JDK) 7](http://www.oracle.com/technetwork/java/javase/downloads/index.html) release.

* * *

[`ShapeMover.java`](examples/ShapeMover.java) 包含此 applet 的完整代码。

调用`contains`方法以确定当按下鼠标时光标是否在矩形的边界内。如果是，则更新矩形的位置。

```
public void mousePressed(MouseEvent e){
    last_x = rect.x - e.getX();
    last_y = rect.y - e.getY();
    if(rect.contains(e.getX(),
        e.getY())) updateLocation(e);
    ...

public void updateLocation(MouseEvent e){
    rect.setLocation(last_x + e.getX(),
        last_y + e.getY());
    ...
    repaint();

```

您可能会注意到在每个鼠标位置重绘`Shape`的速度很慢，因为每次移动时都会重新渲染填充的矩形。使用双缓冲可以消除这个问题。如果使用 Swing，绘图将自动双缓冲;您根本不必更改渲染代码。该程序的 Swing 版本的代码是 [`SwingShapeMover.java`](examples/SwingShapeMover.java) 。

## 示例：HitTestSample

此应用程序通过在用户单击`TextLayout`的任何位置绘制默认插入符来说明命中测试，如下图所示。

&lt;applet alt="HitTestSample applet" archive="examples/lib/HitTestSampleApplet.jar" code="HitTestSample" height="250" width="400"&gt;&lt;param name="permissions" value="sandbox"&gt;&lt;/applet&gt;

* * *

**Note:**  If you don't see the applet running, you need to install at least the [Java SE Development Kit (JDK) 7](http://www.oracle.com/technetwork/java/javase/downloads/index.html) release.

* * *

[``HitTestSample.java`` ](examples/HitTestSample.java)包含此 applet 的完整代码。

`mouseClicked`方法使用`TextLayout.hitTestChar`返回`java.awt.font.TextHitInfo`对象，该对象包含`TextLayout`对象中的鼠标单击位置（插入索引）。

`TextLayout` `getAscent`，`getDescent`和`getAdvance`方法返回的信息用于计算`TextLayout`对象的原点位置，使其水平和垂直居中。

```
...

private Point2D computeLayoutOrigin() {
  Dimension size = getPreferredSize();
  Point2D.Float origin = new Point2D.Float();

  origin.x = (float) (size.width -
             textLayout.getAdvance()) / 2;   
  origin.y = 
    (float) (size.height -
             textLayout.getDescent() +
             textLayout.getAscent())/2;
  return origin;
}

...

public void paintComponent(Graphics g) {
  super.paintComponent(g);
  setBackground(Color.white);
  Graphics2D graphics2D = (Graphics2D) g;                
  Point2D origin = computeLayoutOrigin();
  graphics2D.translate(origin.getX(),
                       origin.getY());

  // Draw textLayout.
  textLayout.draw(graphics2D, 0, 0);

  // Retrieve caret Shapes for insertionIndex.
  Shape[] carets =
      textLayout.getCaretShapes(insertionIndex);

  // Draw the carets.  carets[0] is the strong
  // caret and carets[1] is the weak caret.   
  graphics2D.setColor(STRONG_CARET_COLOR);
  graphics2D.draw(carets[0]);                
  if (carets[1] != null) {
    graphics2D.setColor(WEAK_CARET_COLOR);
    graphics2D.draw(carets[1]);
  }       
}

...

private class HitTestMouseListener
              extends MouseAdapter {

    /**
     * Compute the character position of the
     * mouse click.
     */     
    public void mouseClicked(MouseEvent e) {

      Point2D origin = computeLayoutOrigin();

      // Compute the mouse click location
      // relative to textLayout's origin.
      float clickX =
          (float) (e.getX() - origin.getX());
      float clickY =
          (float) (e.getY() - origin.getY());

      // Get the character position of the
      // mouse click.
      TextHitInfo currentHit =
          textLayout.hitTestChar(clickX, clickY);
      insertionIndex =
          currentHit.getInsertionIndex();

      // Repaint the Component so the new
      // caret(s) will be displayed.
      hitPane.repaint();
    }

```