8

I've been toying around in Android and attempting to port over a Java app. Below are some questions regarding to issues I've run into and would like some guidance on.

It is a rather large question (multiple questions rather). However, I'm not asking them blindly as I have researched what I could about them and attempted to put my understanding to use. I've put time into asking the questions in hopes that they are half-ways clear on what I'm wanting to achieve.

I'll be offering half of my rep as a bounty since I'm hoping for thorough answers and help, hopefully it will be enough to entice a few people to help.

In advance, thank you for your time and help! (looking forward to reading responses).

TextLayout && Font && Graphics2D

In question are the following classes and methods:

  • TextLayout
  • TextLayout.getAdvance()
  • TextLayout.getAscent()
  • TextLayout.draw()
  • Graphics2D.getFontRenderContext()

I'm not quite sure what is equivalent of TextLayout in Android. I had read that some make a TextView and use that, but am unsure if that will

work for the following. I'll provide some source of what I'm wanting to do and perhaps one can help me.

Java Source:

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 Replication Attempt:

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);

Im stuck at determining how to create a TextLayout and what to do for the method getAdvance(). I noticed that in Paint.FontMetrics() there are

some possible alternatives, but I don't know if any compare.

I'm also unsure how to deal with the following Java code:

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);

Review/Summary of Questions Above:

  • What is an Android alternative for TextLayout?
  • What is equivalent to TextLayout.getAdvance()? (Am I able to use fontMetrics to achieve it?)
  • Are there Android equivalents to Graphics2D.getFontRenderContext()?
  • Can you provide example source for Android?

This is currently one of my biggest issues with porting Java over to Android. I would be greatful for any help, advice, examples, etc.

Font

Below are the methods I am wanting to replicate that deal with font, textlayout, and graphics2d. The first source is the Java methods and

below it is my attempt to replicate it.

In question are the following classes and methods:

  • Font.deriveFont(float size) Creates a new font objects by replicating the current font object and applying a new style to it
  • TextLayout.getAdvance() The advance is the distance from the origin to the advance of the rightmost (bottommost) character measuring in the line direction
  • Graphics2D.setRenderingHint(RenderingHints, RenderingHints)
  • Graphics2D.getFontRenderContext() Encapsulates application hints such as anti-aliasing and fractional metrics

Java Source:

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 Attempt:

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;            
}

Final Questions About The Above Methods:

  • What alternative do I have for TextLayout.getAdvance()? (Ignore if it has been answered due to the question regarding TextLayout)
  • What alternative do I have for Graphics2D.getFontRenderContext()?
  • Does my Android source replicate the Java source? If not, what needs to be changed?
  • Are there better ways of doing this? If so, how?

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

Is there a way to make a subclass of oval to create something equal to Java Ellipse2D.Double? If so, how would one go about it?

ComponentAdapter && ComponentEvent

I have these in java because my component is able to be resized, in Android what are the equivalents of these for views? (if any)

Shlublu
  • 10,917
  • 4
  • 51
  • 70
StartingGroovy
  • 2,802
  • 9
  • 47
  • 66
  • I would suppose this would have got better answers as multiple different questions ... but now your bounty reputation is already spent :-( (For me, I don't know anything about Android, so I can't really help.) – Paŭlo Ebermann Jul 27 '11 at 21:30
  • @Paŭlo Ebermann I was thinking about breaking it into 3-4 different questions but I didn't have enough bounty points to do so and the fact that one can only run 1 bounty at a time would delay the answering further. I'm hoping an Android guru will come along and be able to help in most (if not all sections). :) – StartingGroovy Jul 27 '11 at 21:38
  • Isn't it better for you to use some Android native UI definition ways such as resource XML for the like of Widgets and Layout? Some layout such as RelativeLayout might help you to locate in your canvas. – Wei Jul 28 '11 at 16:28
  • @Winston Do you mind elaborating a bit? – StartingGroovy Jul 28 '11 at 17:53

4 Answers4

3

The TextLayout, Font and Graphics2D question can be achieved simply in Android by using a TextView, some layout attributes in the layout xml file and possibly augmenting it with some code. To illustrate with an example, the TextView could be declared in the layout xml as follows:

<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" />

Most of the attributes are self-explanatory, but the layout_width hasn't been set as we'll augment the TextView with a Font which will affect the width it takes up.

AssetManager assetManager = getContext().getAssets();       

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

TextView logo = (TextView)findViewById(R.id.logo);
logo.setTypeface(tf);

The setTypeface() method is overloaded with an additional style parameter which can also provide bold and/or italic effects.

The specific position this text is drawn on-screen will be dependent on the layout or combinations of layouts you choose e.g. RelativeLayout, AbsoluteLayout, etc - there are a lot of resources which can teach you how to use these to good effect.

If this is too limited, then you can draw on the Canvas directly. Here, you can specify a Paint, or TextPaint object, where you can set anti-aliasing and several other paint effects.

In place of Ellipse2D.Double, you could use Canvas.drawOval(RectF oval, Paint paint) where the RectF object specifies the bounding box for the oval.

To enable views (components) to be resized automatically, you should use where possible, flexible layout attributes such as wrap_content, match_parent or fill_parent rather than specific 'dip' sizes. Graphics should be converted to 9-patch formats so that they can stretch to accommodate size changes.

If you really need to calculate the length of text, or specify text size to fit a specific physical space, then you can refer to this answer on SO.

Community
  • 1
  • 1
John J Smith
  • 11,435
  • 9
  • 53
  • 72
  • Thank you for the detailed answer, I will check into doing a `TextView`. Doing it this way, will I still be able to resize my text via the method I provided above? (might be necessary for different devices and whether the View is resized). I feel that for now the pre-defined layouts may be too constrictive so I'll draw the text right to the Canvas. `Canvas.drawOval(RectF, paint)` seems to work well, but is there a way to do it with a Path? I'll have to check into 9-patch (i just pulled up an article) and get back to you on that. – StartingGroovy Jul 28 '11 at 20:14
  • I'll also check into the SO link you provided me with. On a side note, how bad of practice is it that I use very little xml compared to Java (largely procedural design and not so much declarative). **EDIT** As for the Ellipse and Path, I think I could simply do `Path.addOval(RectF oval, Path.Direction dir)` – StartingGroovy Jul 28 '11 at 20:23
  • The text can be resized at any time by setting the view.setTextSize or Paint.setTextSize - this can be constrained by the size of the device or other layout settings. I think the choice of xml over java code for layout is personal preference - both have their pros and cons. You can use a Path along which to draw text using Canvas.drawTextOnPath(). – John J Smith Jul 28 '11 at 21:18
  • Your post has been quite helpful. I appreciate all the help you have given. I'm going to award you the bounty as well as the answer. I will also be making another question shortly about drawing my Ellipse, perhaps you'll have time to take a stab at it :) Thanks again for all your help! – StartingGroovy Aug 01 '11 at 19:21
2

There is no exact equivalent to TextLayout but you can use FontMetrics (see the Paint) class to get the advance and ascent. To draw text, simply use a Canvas. There is no equivalent of FontRenderContext (it's not needed on Android.)

Graphics2D's rendering hints equivalents are simply properties on the Paint class. Font.deriveFont() has no equivalent, simply set the appropriate properties on Paint.

Ovals are drawn using a Canvas, there is no Oval class. You can use a Path instance to do the same thing though.

Romain Guy
  • 97,993
  • 18
  • 219
  • 200
  • Thank you for the reply. What is your suggestion exactly for `TextLayout`? (sorry I personally haven't used TextLayouts before and am not too familiar with them). Also, I couldn't find `getAdvance()` in the Paint class, I looked prior to posting. As for `Font.deriveFont()` so my Android attempt in the post above is sufficient? Lastly, is there an example of how to use Path to create an Ellipse? By the way, I have visited your site a few times since looking into Android :) – StartingGroovy Jul 28 '11 at 19:03
  • You can get the advance by using Paint.measureText(). For an ellipse, just look at the documentation for Path, it's obvious :) – Romain Guy Jul 28 '11 at 21:29
1

I think you might be looking for android.text.layout

Moog
  • 10,193
  • 2
  • 40
  • 66
  • thank you for pointing me there. I'm not sure how I overlooked that! I will see if this can be an alternative to John's advice of the TextView. The main thing I see that might be beneficial is the `getWidth()` method, perhaps that is somewhat similar to `getAdvance()`? – StartingGroovy Jul 28 '11 at 20:20
1

This is a comment to John J Smith's answer, but it's too long for the comment box.

About java vs. XML for layouts: Android's very flexible in this regard, and for view creation they have pretty much feature parity. However, I would highly recommend you look into using XML for at least the basic layouts, as doing the same in code is error prone and gets verbose very quickly. You can always grab references to these view elements later, and adjust them in code. Also, when catering for different form-factors, a nice feature of android is that you can specify unique layouts according to the device resolution and pixel density, and the selection of these layouts and appropriate resources (such as images) is done automatically by the system. An additional benefit is you'll have Spring-like (but better) layout-editing, which is much faster for making small changes than loading a compiled application onto a device or emulator.

  • Can you explain what you mean by `error prone` or perhaps provide an example? As for using XML, I have a lot of constant strings and strings in general, would you recommend I store all my strings in xml rather than in my classes and use `getResources()` to fetch them? I have been debating about this for awhile – StartingGroovy Aug 01 '11 at 19:25
  • By error prone, I'm pretty much talking about layout managers (that size + place view elements). Their behaviour isn't always intuitive - have a quick play with relativelayouts in the layout editor to see what I mean. You'll save yourself hours of frustration by using a visual editor. As for verbose, setting a static height and width of a view element in code is something like this: –  Aug 04 '11 at 02:44
  • `LayoutParams lp = new LinearLayout.LayoutParams(getContext(), 640, 480); nyView.setLayoutParams(lp);` The XML equivalent is `android:width="640px" android:height="480px"`. For strings, yes, definitely use XML for anything user-facing. You organise your resources (this includes strings) into folders corresponding to languages and device specifics such as resolution. This is quite nice, as it makes multi-language support easy as the correct string assets are loaded dynamically at run-time. –  Aug 04 '11 at 02:55