Android Multi Column Text - android

Is there any way in which I can display a long text on multiple columns?
For example I need to display an article, I have the entire String and I want to place it on 3 columns. How can I achieve this? Are there any libraries that can help me or do you know how should I approach this problem?
Any suggestion is appreciated. Thanks.
EDIT: The issue is the splitting of the string not the layout. I know I can use TableLayout... or weights... to distribute column evenly and so on. The problem is how do I split the String properly. Maybe 2 column would get filled and the 3rd just half of it? I don't know how to approach this, not the actual layout.

Check the TableLayout. To split your text you could split your text after 1/3 of your count of chars. Of cource you have to split the text at one whitespace charecter. UPDATE: See also my example code at the end of my posting.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="#string/table_layout_4_open"
android:padding="3dip" />
<TextView
android:text="#string/table_layout_4_open_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
<TableRow>
<TextView
android:text="#string/table_layout_4_save"
android:padding="3dip" />
<TextView
android:text="#string/table_layout_4_save_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
</TableLayout>
For splitting you can test this code. The algorithem could maybe a little better be nearer to the split boundaries, but it works.
public static String[] getRows(String text, int rows) {
// some checks
if(text==null)
throw new NullPointerException("text was null!");
if(rows<0 && rows > 10)
throw new IllegalArgumentException("rows must be between 1 and 10!");
if(rows==1)
return new String[] { text };
// some init stuff
int len=text.length();
int splitOffset=0;
String[] ret=new String[rows];
Pattern whitespace = Pattern.compile("\\w+");
// do the work
for(int row=1;row<rows;row++) {
int end;
int searchOffset=len/rows*row;
// search next white space
Matcher matcher = whitespace.matcher(text.substring(searchOffset));
if(matcher.find() && !matcher.hitEnd()) {
// splitting on white space
end=matcher.end()+searchOffset;
} else {
// hard splitting if there are no white spaces
end=searchOffset;
}
ret[row-1]=text.substring(splitOffset, end);
splitOffset=end;
}
// put the remaing into the last element
ret[rows-1]=text.substring(splitOffset);
return ret;
}

Are you looking for a 'newspaper column' style? Once the first (fixed size) text-box fills, the text should continue into the other and so on?
If so, why not display your long string into a WebView where you can more easily achieve this using HTML and CSS?

use (Calculate text size according to width of text area)
Paint paint = new Paint();
Rect bounds = new Rect();
int text_height = 0;
int text_width = 0;
paint.setTypeface(Typeface.DEFAULT);// your preference here
paint.setTextSize(25);// have this the same as your text size
String text = "Some random text";
paint.getTextBounds(text, 0, text.length(), bounds);
text_height = bounds.height();
text_width = bounds.width();
to calculate how much is your text going to take space.
and calculate view.getWidth() about how much is your view width...
Then do the needful by dividing the text accordingly.

Related

Android: how to measure whether textview having enough space to render textview content

Please find layout
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12345678901234567890"/>
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="12345678901234567890"/>
<TextView
android:id="#+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="12345678901234567890"/>
Screenshot:
What I want is to move textview at textview or add new textview at runtime? but more importantly how I can measure whether textview have enough space in right side to render my content.
titleTxt.post {
val lineCount = titleTxt.lineCount
if (lineCount > 1) {
// Already break in 2 lines, update the content
// Do whatever you want with your content here
val textBuilder = StringBuilder()
for (line in 0 until lineCount) {
val layout = titleTxt.layout
textBuilder.append(titleTxt.text.subSequence(layout.getLineStart(line), layout.getLineEnd(line)))
.append("\n")
}
titleTxt.text = textBuilder
// Here I lower my font size so the overall height is not too big
titleTxt.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimensionPixelSize(R.dimen.font_large).toFloat())
}
}
Here is what I do in my project:
I check the line count
If it's more than 2 then it doesn't have enough space in 1 line for your content.
Use the layout instance to get the substring of any line you want. Do whatever you want with your content
Suppose your wrapper layou is a LinearLayout, you may use this code to make sure whether have enought space in horizontal:
wrapperLayout.post{
if(wrapperLayout.width > textView1.width + textView2.width + textView3.width) {
//the space is enougth to contain three textview
} else {
//the sapce is not enougth
}
}

how can i count the character limit of TextView?

I want to count the total number of characters a TextView can hold with 14dp text size and 100dp of width and with 100dp of height. then I have to add those characters' limits to an EditText.
<TextView
android:id="#+id/OFSO_text1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_weight="1"
android:layout_gravity="top|center"
android:fontFamily="#font/lato_bold"
android:gravity="center|bottom"
android:text="SEASONAL DISCOUNT ARE HERE"
android:textAlignment="center"
android:textAllCaps="true"
android:textColor="#color/lightcream"
android:textSize="14dp"
android:textStyle="normal"
android:autoSizeTextType="none"
/>
I have tried this code but this is not working properly. this provides me more char limit then this Textview can holds.
TextPaint paint = textView.getPaint();
int wordwidth=(int)paint.measureText("a",0,1);
int screenwidth = getResources().getDisplayMetrics().widthPixels;
int maxLength = screenwidth/wordwidth;
please help me out!! thanks in advance.
happy coding!!!
You are missing font size and typeface in your measurements.
// In case you are storing your font in res/font
Typeface typeface = ResourcesCompat.getFont(context, R.font.noto_sans_regular);
paint.setTypeface = typeface
paint.setTextSize = sizeOfYourFontInPixels
First of all, your textholder size varies from device to device,
And for the count, you can do something like,
int textLenght = yourtText.getText().toString().lenght();
if(textLenght>n) {
yourEditText.setText(yourText);}
Measuring Text: You can do something like this :
float textWidth = textView.getPaint().measureText(textView.getText().toString());
if(textView.getMeasureWidth() == n) {
Log.i("Text_count",textView.getText().toString().lenght());
}
or
if(textWidth == n) {
Log.i("Text_count",textView.getText().toString().lenght());
}
Try this
Add addTextChangedListener to the edittext for reading characters.
In override methods beforeTextChanged, onTextChanged and afterTextChanged, read characters like below
int length = etText.length();
int leftCharacters = 500 - length; //let default length of edittext is 500
Hope, it works for you!!

Android: Measured width of dynamically created TextView sometimes wrong

I'm stuck on a strange little problem.
The goal with this activity is to display two texts, one is the original the second the answer text. The answer text contains errors and the user has to find and mark those errors.
The solution we came up with is to split the text into its words and display each word as its own in a TextView. All these TextViews are created dynamically at runtime, because there are many different texts to display.
There are two instances, where we need a 'line break': a) the text contains a linebreak () and b) the width of the display wouldn't fit any more text.
This solution works most of the time but each text has 2-4 words, which don't fit the line width and are therefore broken up into multiple lines visually.
Here's the code:
String[] questionSplit = exercise.exerciseQuestion.split(" ");
ids = new Integer[questionSplit.length];
int displayWidth = getResources().getDisplayMetrics().widthPixels;
int currentLineWidth = 0;
Integer lastIdInRow = 0;
int counter = 0;
for(String bit : questionSplit) {
TextView tv = new TextView(this);
tv.setId(generateViewId());
ids[counter] = tv.getId();
//Exception for <br>
if(bit.equals("<br>")) {
lastIdInRow = ids[counter - 1];
currentLineWidth = 0;
} else {
tv.setText(bit);
tv.setPadding(dpToPx(3), dpToPx(3), dpToPx(2), dpToPx(2));
tv.measure(0, 0);
currentLineWidth += tv.getMeasuredWidth();
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if(currentLineWidth <= displayWidth && counter == 0) {
// move along, nothing to see here
} else if(currentLineWidth <= displayWidth && counter != 0) {
p.addRule(RelativeLayout.RIGHT_OF, ids[counter - 1]);
} else {
lastIdInRow = ids[counter - 1];
currentLineWidth = 0;
}
if(lastIdInRow != 0 && lastIdInRow != tv.getId()) {
p.addRule(RelativeLayout.BELOW, lastIdInRow);
}
rlTextComparisonOriginal.addView(tv,p);
}
counter++;
}
To explain the layout rules of the TextViews: if the measuredWidth fits into the line, a RIGHT_OF the last id rule is added. If it would overflow, a BELOW the last id in the line rule is added.
As I mentioned earlier, for most of the text that works perfectly. But there are some words which do not fit. If I change the displayWidth to be only 80% of the display width, the error persists just the word changes, so I think it's not the specific text / word.
And here is the relevant part of the view's xml
<ScrollView
android:id="#+id/svTextComparisonDesc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tvTextComparisonHeaderMiddle"
android:paddingBottom="55dp"
>
<RelativeLayout
android:id="#+id/rlTextComparison"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvTextComparisonDescription"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#color/text_background"
android:padding="10dp"
android:text="#string/text_exercise_desription"
android:textSize="#dimen/activity_text_description_size"
android:scrollHorizontally="false"
/>
<RelativeLayout
android:id="#+id/rlTextComparisonOriginal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/text_background"
android:layout_below="#id/tvTextComparisonDescription"
/>
<RelativeLayout
android:id="#+id/rlTextComparisonAnswer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/rlTextComparisonOriginal"
/>
</RelativeLayout>
</ScrollView>
The last bit of information: right now the code above resides in the activity's onCreate Method. If I log the measuredWidth and displayWidth and currentWidth, the logic isn't broken, the measuredWidth fits into the line, but after rendering, it doesn't.
Any ideas what the problem actually might be? Thanks in advance!
After using the mentioned lib the code is much cleaner and looks like this:
//Exception for <br>
if(bit.equals("<br>")) {
FlowLayout.LayoutParams lp = new FlowLayout.LayoutParams(0,0);
lp.setNewLine(true);
flTextComparisonOriginal.addView(tv,lp);
} else {
tv.setText(bit);
tv.setPadding(dpToPx(3), dpToPx(3), dpToPx(2), dpToPx(2));
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
flTextComparisonOriginal.addView(tv, p);
}
In case using a library is acceptable instead of coding it yourself, you will find two projects on github under the keyword "FlowLayout". These sound like they solve the layout you need:
https://github.com/ApmeM/android-flowlayout
https://github.com/blazsolar/FlowLayout
To get width and height of the text view,
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text,0,text.length(),bounds);
int height = bounds.height();
int width = bounds.width();

Reduce number of lines in multiline TextView if not all lines fit

I have a vertical LinearLayout with a multiline TextView set to have a layout_weight of 1, and below that a more complex layout that wraps its content. If there isn't enough room in the outer LinearLayout, I want the number of lines shown in the TextView to be reduced. Instead, the bottom half of the bottom line just gets clipped.
Is there any way around this? This is in an app widget, so I can't use custom views.
Simplified layout XML:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|bottom"
android:orientation="vertical" >
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="fill_vertical"
android:maxLines="2" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- ... sized content ... -->
</LinearLayout>
</LinearLayout>
A time ago I had to mess with this too, basically within an IRC client which receives hundreds of lines per tab, and sadly, the response is no, there's not any mechanism to automatically control the buffer of the TextView.
So I had to implement an infinite loop Thread (which I would later convert into a Service) which takes the number of lines of the current TextView and if it exceeds a certain amount, it's truncated from the beggining of the buffer (the oldest lines get removed).
I'll attach what have I done, maybe it might help you.
static void FreeTextBuffer() {
final TextView tv = (TextView) findViewById(R.id.your_textview);
final Spanned sptxt = (Spanned) tv.getText();
// This is a workaround, seems that Html.toHtml() passing directly the text makes the system stale - this works
final SoftReference<String> texto = new SoftReference<String>(Html.toHtml((Spanned) sptxt.subSequence(0, sptxt.length())));
final int matches = StringUtils.countMatches(texto.get(), "<br>");
if (matches > (3 * maxLines) / 2) {
// Here I'm counting the amount of \n to cut off (the 0.5 * matches is a tuned solution that works for me)
int cutpoint = -1;
for (int i = 0; i < 0.5 * matches; i++)
cutpoint = texto.get().indexOf("<br>", (cutpoint < 0)? 0 : cutpoint + 1);
// Just for the Thread...
final int forUIth = cutpoint;
if (cutpoint > -1) {
((Activity) globvars.getContext()).runOnUiThread(new Runnable() {
public void run() {
synchronized(tv) {
tv.setText(Html.fromHtml(texto.get().substring(forUIth)));
}
}
});
}
}
}

how to know if the string have ellipsize

i have a string in the textView as:
<TextView
android:id="#+id/comment_concent"
android:maxLines="2"
android:textColor="#FFCD3333"
android:ellipsize="end"
android:layout_marginRight="5dip"
android:layout_width="210dip"
android:layout_height="50dip"
android:textSize="15sp"
android:text="aaddadadddddddddddddddddddddddddddddddddddddadddddddd"/>
sometime the text only "addadaddad",in other words the string length is not fixed,so if the string is too long,you know the end is show"...",my question is that how to know the string if end with"...",if the string is end with"...", i will call other methods to show the whole string.
Measure the text using paint object to see if it width is greater than textviews.
Paint paint = new Paint();
paint.setTextSize(textView.getTextSize());
final float size = paint.measureText(textview.getText());
if ((int)size > textView.getWidth()) {
// text is elipsized.
}
Edit:
Even better, just set textsize from textview instead of calculating, then measure text. Edited the above code.
Please look into this code. This will help you.
TextView textView=(TextView) yourtextviewId;
Layout l = textView.getLayout();
if ( l != null){
int lines = l.getLineCount();
if ( lines > 0)
if ( l.getEllipsisCount(lines-1) > 0)
Toast.makeText(getApplicationContext(), textView.getText().toString(), Toast.LENGTH_LONG).show();
}

Categories

Resources