将Java类和方法移植到Android平台。(TextLayout、Font、Graphics2D等)

8
我一直在尝试在Android上移植一个Java应用,并遇到了一些问题,以下是我遇到的问题并希望得到指导。
这是一个相当大的问题(多个问题),但我并不是盲目提问,因为我已经尽力研究过它们并尝试了理解。我花了时间来提出问题,希望能清楚地表达出我想要实现的目标。
我将提供我的声誉作为赏金,因为我希望得到详细的答案和帮助,希望这足以吸引一些人来帮助。
提前感谢您的时间和帮助!(期待回复)
TextLayout&Font&Graphics2D
涉及以下类和方法:
- TextLayout - TextLayout.getAdvance() - TextLayout.getAscent() - TextLayout.draw() - Graphics2D.getFontRenderContext()
我不太确定在Android中等价于TextLayout的是什么。我曾经读到有些人创建TextView并使用它,但不确定是否适用于以下情况。我会提供一些我想做的东西的源代码,也许有人可以帮助我。
Java源代码:
private Font myStringFont = new Font("Helvetica", Font.BOLD, 12);
private String myString = "My Test String";
private int midX = getWidth() / 2;
private int midY = getHeight() / 2;
Graphics2D g2 = new Graphics2d();

TextLayout layout = new TextLayout(myString, font, g2.getFontRenderContext());
g2.drawString(myString, midX - ((int)layout.getAdvance() /2), midY);

Android复制尝试:

Canvas canvas;
Paint paint;
private String myString = "My Test String";
private float midX = getWidth() / 2;
private float midY = getHeight() / 2;
//Unsure what to do about TextLayout <- this is where I need an alternative
canvas.drawText(myString, midX - /* whatever my alternative to layout.getAdvance() is */ /2), midY);

我卡在如何创建TextLayout以及怎么处理getAdvance()方法上。我注意到在Paint.FontMetrics()中有一些可能的替代方案,但不确定哪个更好。此外,我也不确定如何处理以下Java代码:
Graphics2D g2 = new Graphics2d();
private int midX = getWidth() / 2;
private int midY = getHeight() / 2;

TextLayout layout = new TextLayout(myString, g2.getFont(), g2.getFontRenderContext());
layout.draw(g2, midX, MidY);

以上问题的回顾/总结:

  • TextLayout 的 Android 替代品是什么?
  • 相当于 TextLayout.getAdvance() 的方法是什么?(我能否使用 fontMetrics 实现它?)
  • 是否有 Android 等效于 Graphics2D.getFontRenderContext() 的方法?
  • 你能提供 Android 的示例源代码吗?

这是我在将 Java 移植到 Android 时遇到的最大问题之一。我会非常感激任何帮助、建议、示例等。

字体

下面是我想要复制处理 fonttextlayoutgraphics2d 的方法。第一个源是 Java 方法,

下面是我尝试复制它。

以下类和方法存在疑问:

  • Font.deriveFont(float size) 通过复制当前字体对象并对其应用新样式来创建新字体对象
  • TextLayout.getAdvance() Advance 是从原点到测量线方向上最右(最下)字符的前进距离
  • Graphics2D.setRenderingHint(RenderingHints, RenderingHints)
  • Graphics2D.getFontRenderContext() 封装应用程序提示,如抗锯齿和分数度量

Java 源代码:

private String myString = "Print this test statement";
private int myStringFontSize = 15;
private Color myStringFontColor = Color.red;
private Font myStringFont = new Font("Helvetica", Font.BOLD, myStringFontSize);
private int midX = getWidth() / 2;
private int midY = getHeight() / 2;

public drawString(Graphics2D g2) {
    g2.setFont(myStringFont.deriveFont(determineFontSize(g2, myString)));
    g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALISING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    TextLayout layout = new TextLayout(myString, g2.getFont(), g2.getFontRenderContext());

    g2.setPaint(myStringFontColor);
    g2.drawString(myString, midX - ((int) layout.getAdvance() / 2), midY);
}

protected float determinFontSize(Graphics2D g2, String myString) {
    int space = getWidth();
    float fontSize = 1.0f;
    float finalFontSize = fontSize;

    while(fontSize < 25) {
        Font font myString.deriveFont(fontSize);
        Textlayout layout = new TextLayout(waitingMessage, font, g2.getFontRenderContext());

        if(layout.getAdvance() > space) {
            finalFontSize = fontSize - 2;
            break;
        }
        fontSize++;
    }
    finalFontSize = fontSize - 4;
    return finalFontSize;
}

Android 尝试:

private String myString = "Print this test statement";
private int myStringFontSize = 15;
private int myStringFontColor = Color.RED;  //Android uses int rather than Color
Typeface tf = new Typeface();               //Android uses Typeface rather than Font  
private float midX = getWidth() / 2;        //Changed to float because drawText requires float
private float midY = getHeight() / 2;       //changed to float because drawText requires float


public drawString(Canvas canvas, Paint paint) {
    tf.create("Helvetica", BOLD);
    paint.setTypeface(tf);
    paint.setTextSize((float) myStringFontSize);

    paint.setTextSize(determineFontSize(canvas, myString, paint);
    paint.setAntiAlias(true);
    //NOT SURE WHAT TO DO WITH TextLayout YET

    paint.setColor(myStringFontColor);
    canvas.drawText(myString, midX - ((int)layout.getAdvance() / 2), midY, paint);  //Not sure how to deal with layout.getAdvance() just yet    

}

protected float determineFontSize(Canvas canvas, String myString, Paint paint) {
    float fontSize = 1.0f;
    float finalFontSize = fontSize;
    int space = getWidth();

    while(fontSize < 25) {
        paint.setTextSize(fontSize);
        //NOT SURE WHAT TO DO ABOUT TextLayout.getAdvance() YET or g2.getFontRenderContext()

        if(layout.getAdvance() > space) {
            finalFontSize = fontSize - 2;
            break;
        }
        fontSize++;
    }
    finalFontSize = fontSize - 4;
    return finalFontSize;            
}

上述方法的最终问题:

  • TextLayout.getAdvance()有什么替代方案?(如果已经在关于TextLayout的问题中回答,请忽略)
  • Graphics2D.getFontRenderContext()有什么替代方案?
  • 我的Android源代码是否复制了Java源代码?如果没有,需要更改哪些内容?
  • 有更好的方法吗?如果有,怎么做?

Elipse2D.Double(double x, double y, double w, double h)

有没有一种方法可以创建一个椭圆的子类,以创建与Java Ellipse2D.Double相等的东西?如果有,应该如何操作?

ComponentAdapter && ComponentEvent

我在Java中使用它们是因为我的组件可以调整大小,在Android中,视图的等效物是什么?(如果有的话)


我想如果将其拆分成多个不同的问题,可能会得到更好的答案......但现在您的悬赏声望已经花费了 :-((对于我来说,我对Android一无所知,所以无法真正帮助您)。 - Paŭlo Ebermann
@Paŭlo Ebermann,我在考虑将其分成3-4个不同的问题,但我没有足够的赏金点来这样做,而且每次只能运行1个赏金会进一步延迟回答。我希望一个Android大师会出现并能够在大多数(如果不是所有)部分提供帮助。 :) - StartingGroovy
你不觉得最好使用一些Android本地UI定义方式,例如像小部件和布局这样的资源XML吗?一些布局,例如RelativeLayout,可能会帮助你在画布中定位。 - Wei
@Winston 你介意再详细解释一下吗? - StartingGroovy
4个回答

3

在Android中,可以通过使用TextView、布局xml文件中的一些布局属性以及可能的代码来简单实现TextLayoutFontGraphics2D问题。举个例子,可以在布局xml中声明TextView如下:

<TextView android:id="@+id/logo"
    android:layout_width="0dip"
    android:layout_height="wrap_content"
    android:paddingTop="5dip"
    android:text="Fancy Logo"
    android:textSize="24sp"
    android:layout_weight="0.8"
    android:textStyle="bold"
    android:textColor="@color/black" />

大部分属性都是不言自明的,但是layout_width没有被设置,因为我们将使用字体来增强TextView,这将影响它所占据的宽度。

AssetManager assetManager = getContext().getAssets();       

Typeface tf = Typeface.createFromAsset(assetManager,"GILB.TTF");                

TextView logo = (TextView)findViewById(R.id.logo);
logo.setTypeface(tf);
setTypeface()方法重载了一个额外的样式参数,可以提供加粗和/或斜体效果。
此文本在屏幕上的具体位置将取决于您选择的布局或布局组合,例如RelativeLayoutAbsoluteLayout等 - 有很多资源可以教您如何有效地使用它们。
如果这太过于有限,则可以直接在画布上绘制。在这里,您可以指定一个Paint或TextPaint对象,您可以设置抗锯齿和几种其他的画笔效果。
Ellipse2D.Double的位置,您可以使用Canvas.drawOval(RectF oval, Paint paint),其中RectF对象指定椭圆的边界框。
为了使视图(组件)能够自动调整大小,您应该尽可能使用灵活的布局属性,例如wrap_contentmatch_parentfill_parent,而不是特定的“dip”大小。图形应转换为9-patch格式,以便它们可以拉伸以适应大小更改。
如果您真的需要计算文本的长度,或者指定文本大小以适合特定的物理空间,那么您可以参考SO上的这个答案。

谢谢您提供详细的答案,我会尝试使用“TextView”。这种方法是否仍然可以通过上述提供的方法调整文本大小?(可能对于不同的设备和视图大小调整是必要的)。我觉得现在预定义的布局可能太过约束性了,所以我会直接在画布上绘制文本。“Canvas.drawOval(RectF, paint)”似乎效果很好,但有没有用Path的方法呢?我需要查一下9-patch(我刚刚找到了一篇文章),然后再回复您。 - StartingGroovy
我也会查看你提供给我的SO链接。顺便说一下,如果我使用的XML比Java少得多(主要是过程化设计而不是声明式设计),这种做法有多糟糕?编辑关于椭圆和路径,我认为我可以简单地使用Path.addOval(RectF oval, Path.Direction dir) - StartingGroovy
文本可以通过设置view.setTextSize或Paint.setTextSize随时调整大小 - 这可能会受到设备大小或其他布局设置的限制。我认为在布局方面,使用xml而不是java代码是个人偏好 - 两者都有其优缺点。您可以使用路径来绘制文本,使用Canvas.drawTextOnPath()。 - John J Smith
你的帖子非常有帮助。我感谢你所提供的所有帮助。我将授予你赏金和答案。我很快也会提出另一个问题,关于如何绘制我的椭圆形,也许你有时间尝试一下 :) 再次感谢你的所有帮助! - StartingGroovy

2

没有TextLayout的确切等价物,但是您可以使用FontMetrics(请参见 Paint)类来获取advance和ascent。要绘制文本,只需使用Canvas即可。在Android上不需要FontRenderContext的等效项。

Graphics2D的渲染提示等效项仅是Paint类上的属性。 Font.deriveFont()没有相应的等效项,只需在Paint上设置适当的属性即可。

椭圆形可以使用Canvas进行绘制,没有Oval类。但是,您可以使用Path实例完成相同的工作。


谢谢您的回复。对于TextLayout,您有什么具体建议?(抱歉,我个人以前没有使用过TextLayouts,并不太熟悉它们)。此外,在发布之前,我找不到Paint类中的getAdvance()。至于Font.deriveFont(),那么我的Android尝试是否足够了?最后,有没有关于如何使用路径创建椭圆的示例?顺便说一句,自从研究Android以来,我已经多次访问过您的网站 :) - StartingGroovy
你可以使用Paint.measureText()来获取进度。对于椭圆形,只需查看Path的文档,很明显 :) - Romain Guy

1

这是对John J Smith答案的评论,但是它太长了,无法放在评论框中。

关于Java与XML的布局:Android在这方面非常灵活,在视图创建方面,它们几乎具有相同的功能。然而,我强烈建议您至少考虑使用XML来创建基本布局,因为在代码中进行相同操作容易出错,并且很快就会变得冗长。您始终可以稍后获取对这些视图元素的引用,并在代码中进行调整。此外,当针对不同的形态因素时,Android的一个好处是您可以根据设备分辨率和像素密度指定唯一的布局,并且系统会自动选择这些布局和适当的资源(例如图像)。另一个好处是您将拥有类似Spring的(但更好的)布局编辑器,这比将编译应用程序加载到设备或模拟器上要快得多,特别是在进行小改动时。


你能解释一下什么是“容易出错”的意思吗?或者可以举个例子吗?至于使用XML,我有很多常量字符串和普通字符串,您建议我将所有字符串存储在XML中,而不是在我的类中,并使用getResources()来获取它们吗?我已经考虑了一段时间。 - StartingGroovy
错误容易发生,我指的基本上是布局管理器(用于设置视图元素的大小和位置)。它们的行为并不总是直观的 - 在布局编辑器中快速尝试相对布局即可明白我所说的。使用可视化编辑器可以节省你数小时的挫败感。至于冗长,通过代码设置视图元素的静态高度和宽度是这样的: - user668660
LayoutParams lp = new LinearLayout.LayoutParams(getContext(), 640, 480); nyView.setLayoutParams(lp);XML的等效代码为android:width="640px" android:height="480px"。对于字符串,绝对使用XML来展示用户界面。您可以将资源(包括字符串)组织到相应的文件夹中,使其与语言和设备规格(如分辨率)对应。这非常好,因为它使得多语言支持变得轻松,正确的字符串资产会在运行时动态加载。 - user668660

1

谢谢指出那里。我不确定自己是怎么忽略了那个!我会看看这是否可以替代 John 的 TextView 建议。我觉得可能有益的主要是 getWidth() 方法,或许那与 getAdvance() 有点相似? - StartingGroovy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接