6

I have a rectangular tile, and I want to fit an image and some text into it. The image must not overlap the text, and the sizes of the image and the text can vary.

The process must be hand-coded, since we have to fine-tune it according to our client's needs.

I tried the approach of first measuring the rendered text bounds using getTextBounds() or measureText() and then adapting the font size and the image size so they don't overlap.

This works fine if the text is only on one line.

But if TextView wraps the text onto multiple line, I cannot predict the text bounds, since I don't know where TextView would insert the automatic line breaks.

How can I find out the positions in the text where TextView inserts an automatic line break?

Example: Given the text

Lorem ipsum dolor sit amet

which would be rendered as

| Lorem ipsum    |
| dolor sit amet |

I need a function that converts

Lorem ipsum dolor sit amet

to

Lorem ipsum \ndolor sit amet

Jonas Sourlier
  • 13,684
  • 16
  • 77
  • 148

3 Answers3

6

TextView (Layout object) has some useful functions for what you are trying to accomplish:

http://developer.android.com/reference/android/text/Layout.html

take a look at:

getLineCount()

getLineEnd(int line)

You can get the substring for the TextView string based on where the character is located at each lineEnd.

You will need to use getViewTreeObserver() to wait until the TextView is drawn before you can call these and get useful information from them.

Alternatively, you can build a custom TextView that might provide the data through built-in methods or by adding a listener to it. An example of a custom TextView that modifies text size is here:

android ellipsize multiline textview

I've used that and did similar modifications (like what you are trying to do).

Community
  • 1
  • 1
Jim
  • 10,172
  • 1
  • 27
  • 36
  • If the _TextView_ is already drawn, you must call it from a different thread or else the line count won't be correct. `textview.post(new Runnable{ int lineCount = textview.getLineCount(); });` – Juan José Melero Gómez Jan 11 '17 at 20:08
4

You'd probably want to wrap this logic into a custom view (overriding onSizeChanged()) but you can use the Layout class to check where each line ends:

textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Remove immediately so it only fires once
        textView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

        // View should be laid out, including text placement
        final Layout layout = textView.getLayout();
        float maxLineWidth = 0;

        // Loop over all the lines and do whatever you need with
        // the width of the line
        for (int i = 0; i < layout.getLineCount(); i++) {
            maxLineWidth = Math.max(maxLineWidth, layout.getLineWidth(i));
        }
    }
});
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
0

Create an TextWatcher and extract the single lines

@Override
public void afterTextChanged(Editable editable) {
 String str =  editable.toString();
 Layout layout = textView.getLayout();

 ArrayList<String> lines = new ArrayList<>();
 for (int i = 0; i < textView.getLineCount(); i++) 
 {
    int lineStart = layout.getLineStart(i);
    int lineEnd = layout.getLineEnd(i);

    String lineString = str.substring(lineStart, lineEnd);
     lines.add(lineString);
   }
   // now you have the single lines
}
Gugelhupf
  • 943
  • 12
  • 16