Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Layout: Wrap two textview

I would like to achieve the following layout

AAAAAAAAAAAAAAAAAA
AAAAA... BBBBBBBBB

Where AAAA is a textview (ellipsize = "end") and BBB is a textview as well. It will never have more than two lines. I just need the first textview to be wrapped around the second sort of.

How should I do that?

Idea in screenshot

like image 304
Stephane Maarek Avatar asked Sep 26 '14 00:09

Stephane Maarek


2 Answers

Final result

enter image description here

Explanation

It is by no means perfect and will likely need some tweaking for it to fit your needs. I left out the ellipsize part to make it more generic, but it would be easy to adapt the code for that. The idea is simple but it took me a few hours (only because its Sunday) to make it work. We calculate how many more characters from the second TextView would fit the last line of the first one. Then with that number we add the last line to the second TextView with a color matching the background of the container, this is a limitation that could probably be solved somehow yet I can't seem to find it.

With that, we would have the two views with their text aligned, yet they are still not overlapping each-other to allow the wrap effect. So we get the height of the text and add a negative margin to the second one so that they overlap, to ensure that the first TextView is on top of the second one we need to call bringToFront() to avoid having the text with the same color as the background over the last line of the first one (wow this is getting confusing). It may not work good with different fonts and weights (bold for example) since the characters will likely take more space.

I didn't make it a method but it could easily become generic and reusable, just wanted to prove if I could do it or not. Here's the code for anyone and everyone to enjoy and improve upon. The layout is simply a container (a RelativeLayout for example) and two textviews with ids first and second, I've set the text color to red on the second one to make it clearer but it's not necessary.

Code

    final TextView first = (TextView) findViewById(R.id.textView);
    final TextView second = (TextView) findViewById(R.id.textView2);
    final ViewGroup container  = (ViewGroup)findViewById(R.id.container);
    final ViewTreeObserver obs = findViewById(R.id.container).getViewTreeObserver();

    obs.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //Get characters from the first TextView's last line
            String lastLine = first.getText().toString().substring(first.getLayout().getLineStart(first.getLineCount() - 1));

            //Calculate the number of extra characters that will fit in the first one
            int e = 0; //Extra characters
            int lineCount = first.getLineCount();
            while (lineCount == first.getLineCount() && e < second.getText().toString().length() - 1) {
                first.append(second.getText().toString().substring(e, e + 1));
                e++;
            }

            //Remove those characters again to leave the first TextView as it was
            String firstString = first.getText().toString();
            first.setText(firstString.substring(0, firstString.length() - e));

            //Add the last line of the first textview to the second textview
            second.setText(Html.fromHtml("<font color='#F2F2F2'>"+lastLine+"</font>"+second.getText()));

            //add a negative margin to the second textview equal to a line's height
            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) second.getLayoutParams();
            params.topMargin = -(int)(first.getLineHeight()+10);
            second.setLayoutParams(params);

            //Make sure first is on top
            first.bringToFront();

            //Remove the listener
            if (Build.VERSION.SDK_INT < 16) {
                container.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        }
    });
like image 79
Juan Cortés Avatar answered Sep 21 '22 21:09

Juan Cortés


In RelativeLayout make textview AAA normal 2-lines-high textview without ellipsization and make textview BBB with text "... BBBBBBBB" and with non-transparent background so textview AAA will not be visible behind BBB.

Second option is do it programatically - calculate amount of letters and cut that string down if longer than should be.

But none of those 2 "hacks" will work at 100% if you don't use monospace font. Sorry for my english.


<RelativeLayout android:layout_width="200dp" android:layout_height="70dp">

  <TextView android:id="@+id/aaa"
    android:layout_width="match_parent" android:layout="match_parent"
    android:lines="2"
    android:typeface="monospace" />

  <TextView android:id="@+id/bbb"
    android:layout_width="match_parent" android:layout="wrap_content"
    android:typeface="monospace"
    android:lines="1"
    android:background="#ffffffff"
    android:layout_alignParentRight="true"
    android:layout_alignParentBottom="true" />

</RelativeLayout>

You also need to count amount of letters in string for AAA and so in BBB. If sum of those 2 amounts is more than allowed (texts will overlap) add "... " string before BBB string:

BBBTextView = (TextView) findViewbyId(R.id.bbb);

int maxLetterCount = 125; // you need to specify this number
String AAAString = " gtgtfd  def e refrfecj gjv jvrji rf wj jiefijr jgfrjf jrf"
String BBBString = "deeded ed e ddd"

if ( (BBBString.length()+BBBString.length()) > maxLetterCount ) {
    BBBString = "... " + BBBString;
}

BBBTextView.setText(BBBString);
like image 31
Incredible Avatar answered Sep 22 '22 21:09

Incredible