I should split a long text for being shown within as much column as needed. For instance i've got a 80 lines text and i want to show it within two textview longer then larger one next to the other one, each one with a maxlines of 60. Any suggestions for doing that?
I know this is an old question, but even though ...
maybe have a look here :
http://www.accella.net/multi-column-text-displays-in-android/
you need to display your text into a first textview, then get what does not appear in that first textview, then display it in a second textview and so on.
To get the "invisible" text from your textview, here is some code:
private String getInvisibleText(final TextView textView) {
String invisible = null;
int height = textView.getHeight();
int scrollY = textView.getScrollY();
Layout staticLayout = textView.getLayout();
int lastVisibleLineNumber = staticLayout.getLineForVertical(scrollY+height);
int start = staticLayout.getLineEnd(lastVisibleLineNumber);
int end = staticLayout.getLineEnd(textView.getLineCount()-1);
if (textView.getText().toString() != null
&& !textView.getText().toString().isEmpty()
&& end > 0
&& textView.getText().toString().length() >= end) {
invisible = textView.getText().toString().substring(start, end);
}
return invisible;
}
Related
I am using list view to show image and text i want to show like above image, can anyone suggest me how to wrap text around image with out webview. I am using following code:
Drawable dIcon = getResources().getDrawable(R.drawable.video_icon);
int leftMargin = dIcon.getIntrinsicWidth() + 10;
ImageView icon = (ImageView) findViewById(R.id.icon);
icon.setBackgroundDrawable(dIcon);
SpannableString ss = new SpannableString(text);
ss.setSpan(new MyLeadingMarginSpan2(3, leftMargin), 0, ss.length(), 0);
TextView messageView = (TextView) findViewById(R.id.message_view);
messageView.setText(ss);
class
class MyLeadingMarginSpan2 implements LeadingMarginSpan2 {
private int margin;
private int lines;
MyLeadingMarginSpan2(int lines, int margin) {
this.margin = margin;
this.lines = lines;
}
#Override
public int getLeadingMargin(boolean first) {
if (first) {
return margin;
} else {
return 0;
}
}
#Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
int top, int baseline, int bottom, CharSequence text,
int start, int end, boolean first, Layout layout) {}
#Override
public int getLeadingMarginLineCount() {
return lines;
}
};
by using this code iam getting below image pls suggest to how to get first means correct wrapping text around image without more empty spaces
Older post, but since there is no accepted answer and I have just found solution for same problem in my app, I will post a solution.
I have discovered that text without any line break works well.
Text with a line break that splits the text into 2 parts in a way that the part before line break ends to the right of the image, and the part after line break starts already on next line bellow the image, this also works well.
So what I do is I set left margin of the wrapping TextView's LayoutParams to the desired indent, and I set the text into TextView. Then I add OnGlobalLayoutListener, and inside onGlobalLayout callback, I count the position of the last character on the last line to the right of the image
//lines - number of lines to be affected by the leadingMargin
int charCount = textView.getLayout().getLineEnd(Math.min(lines - 1, textView.getLayout().getLineCount() - 1));
If the text does not have more lines than the number of lines that should have the left margin (or if the last character is already line break), I just set the LeadingMarginSpan2 on the whole length of the text.
// s - original Spannable containing the whole text
if (charCount >= s.length() || charCount <= 0 || s.charAt(charCount - 1) == '\n') {
s.setSpan(new MyLeadingMarginSpan(lines, w), 0, charCount, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(s);
}
If the text is longer, I split it into 2 parts (first one ending at the charCount position), insert line break between them, merge them and set the LeadingMarginSpan2 only on the first part.
else {
Spannable s1 = new SpannableStringBuilder(s, 0, charCount);
s1.setSpan(new MyLeadingMarginSpan(lines, w), 0, charCount, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spannable s2 = new SpannableStringBuilder(System.getProperty("line.separator"));
Spannable s3 = new SpannableStringBuilder(s, charCount, s.length());
textView.setText(TextUtils.concat(s1, s2, s3));
}
At the end, do not forget to remove the left margin of the TextView's LayoutParams.
I'm trying to display a bunch of text on the screen by placing TextViews inside rows of single-line LinearLayouts. Each word is stored in its own separate TextView, and I want to be able to place as many TextViews as will fit on a single LinearLayout line and detect when I've run out of horizontal space so that I can move to the next line.
The problem I'm facing is that I can't seem to find a way to measure the changing layout sizes as I create the display, because I can't get a reference width using getWidth() on the parent layout, and even after I add the TextViews, I can't seem to control the width.
We had a working version before, but it did everything using on hard-coded numbers based on the number of characters in a TextView at a fixed size. I'm trying to extend the app to work with all text and screen sizes. If this needs to be completely overhauled, I understand - I just want to be able to fill up the screen with an indefinite number of lines of text.
An obvious solution would be to just place all the text inside one TextView, but we need to be able to access each Word/Ponctuation object and its attributes through the displayed TextViews.
// layout_row is the current LinearLayout row I'm adding my TextViews to
// layout is the LinearLayout parent of all layout_rows
// text.text_content is a linked list of Word and Ponctuation objects
// each Word and Ponctuation object has a TextView attribute called view
private void display_views() {
if (text != null)
{
boolean prochainLigneSuivante; // if new line is to follow
int widthSoFar = 0;
int layoutWidth = layout_row.getWidth();
for (Object o : text.text_content) {
if (o instanceof Word ) {
Word w = (Word) o;
Object next = text.next(o);
if (noNeedForSpace(w)) {
// by default all TextViews have
// right padding to simulate spaces
w.view.setPadding(0, 0, 0, 0);
}
layout_row.addView(w.view);
widthSoFar += w.view.getWidth();
// Am I out of space?
prochainLigneSuivante = widthSoFar >= layoutWidth;
if(prochainLigneSuivante) {
layout_row.removeView(w.view);
widthSoFar = 0;
layout_row = new LinearLayout(context);
layout_row.setOrientation(LinearLayout.HORIZONTAL);
layout_row.addView(w.view);
layout_row.setBackgroundColor(Color.BLACK);
layout_row.setLayoutParams(new
LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
layout.addView(layout_row);
}
}
else if (o instanceof Ponctuation) {
Ponctuation p = (Ponctuation) o;
if (p.text.contains("CR")) {
layout_row = new LinearLayout(context);
layout_row.setOrientation(LinearLayout.HORIZONTAL);
layout_row.setLayoutParams(new
LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.MATCH_PARENT));
widthSoFar = 0;
layout.addView(layout_row);
}
else {
if (p.view.getText().equals(" "))
p.view.setPadding(0, 0, 0, 0);
layout_row.addView(p.view);
if(!p.view.getText().equals(""))
widthSoFar += p.view.getWidth();
}
}
}
}
else {
Log.e("Text", "text est nul");
}
scroll.refreshDrawableState();
transition.startTransition(0);
}
I'm learning how to implement one-page reading style, like in Google Currents, for my app.
The contents(text) are adjusted based on the screen size and there is no scroll, no zoom.
The overflowed text are in the next page. (I knew that it is using ViewPager.)
Here is the screen shoots:
My questions are:
how can I fit(adjust) the contents(text) to the screen?
how can I automatically parse overflowed text to the next screen? (so that the user
will only need to swipe between screens for reading.)
Also, I'm considering to use TextView for the content. (GoogleCurrent used WebView, I think) My app don't need to use WebView. Will it be possible for textview to achieve like that?
I implemented something like this with several TextViews. First of all, I created a ViewPager for swyping between TextViews.
After than I separate long text to several blocks, one per page.
To get text for page I use this function:
TextView tv = new TextView(context); //creating text view
int width = mtv.getWidth() - tv.getPaddingLeft()-tv.getPaddingRight(); //get width for text
int lHeight = tv.getLineHeight(); getting line height
int height = ((View)mtv.getParent()).getHeight() - tv.getPaddingBottom()-tv.getPaddingTop(); // getting height of text
int linesCount = (int)Math.floor((float)height/lHeight); // get line count on page
String tmpText = "";
for (int i =0; i<linesCount; i++)
{
int index = Math.min(mtv.getLayout().getPaint().breakText(text, true, width, new float[0]), text.indexOf('\n')+1); // breaking text to lines. To use this you need mtv to be measured
String t = text.substring(0, index); //getting text for this textview
if (index+1 < text.length() && (text.charAt(index+1)!=' ' || text.charAt(index+1)!='\n') && t.lastIndexOf(" ")>0)
index = t.lastIndexOf(" ")+1; // hack for situation when line starts with " "
text = text.substring(index);//getting text for next iteration
t = t.substring(0, index);
tmpText+=t;
}
//I use spannable string for links and some styles. Recalculating spans for new indexes
SpannableString s = new SpannableString(tmpText);
Object os[] = spanned.getSpans(offset, offset + tmpText.length(), Object.class);
for (Object o: os)
{
int start = spanned.getSpanStart(o)-offset;
int end = spanned.getSpanEnd(o)-offset;
if (start < 0)
start = 0;
if (end>=s.length())
end = s.length()-1;
s.setSpan(o, start, end, spanned.getSpanFlags(o));
}
offset+=tmpText.length();
while (text.startsWith(" ") || text.startsWith("\n"))
{
text = text.substring(1);
offset++;
}
tv.setText(s);
Nad I think, that google uses TextView, not webView. Read about Spanned and Spannable. It's have ability to show links, images, text with different styles in one textView.
I think this is the opposite of
Set width of TextView in terms of characters
I have a TextView where I'm showing some report data. I use a monospace TypefaceSpan for a portion of it because I want columns to line up.
I used my test Android device to figure out how many columns I could fit, but the Android emulator seems to have exactly one less column, which makes things wrap in an ugly way in portrait mode.
Is there a way to find out how many characters should fit on 1 line?
The answer would be to use the breakText() of your textView's Paint Object. Here is a Sample ,
int totalCharstoFit= textView.getPaint().breakText(fullString, 0, fullString.length(),
true, textView.getWidth(), null);
Now totalCharstoFit contains the exact characters that can be fit into one line. And now you can make a sub string of your full string and append it to the TextView like this,
String subString=fullString.substring(0,totalCharstoFit);
textView.append(substring);
And to calculate the remaining string, you can do this,
fullString=fullString.substring(subString.length(),fullString.length());
Now the full code,
Do this in a while loop,
while(fullstirng.length>0)
{
int totalCharstoFit= textView.getPaint().breakText(fullString, 0, fullString.length(),
true, textView.getWidth(), null);
String subString=fullString.substring(0,totalCharstoFit);
textView.append(substring);
fullString=fullString.substring(subString.length(),fullString.length());
}
Well you could do math to find this out, find the width of the character, divide the width of the screen by this, and you'd have what you're looking for.
But is it not possible to design it better? Are there any columns you can group together? Display as a graphic, or even exclude completely?
Another possible solution is to use something like a viewpager. (Find out how many columns' width fit on the first page, and then split the remaining tables onto the second page).
You can get total line of Textview and get string for each characters by below code.Then you can set style to each line whichever you want.
I set first line bold.
private void setLayoutListner( final TextView textView ) {
textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
textView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
final Layout layout = textView.getLayout();
// Loop over all the lines and do whatever you need with
// the width of the line
for (int i = 0; i < layout.getLineCount(); i++) {
int end = layout.getLineEnd(0);
SpannableString content = new SpannableString( textView.getText().toString() );
content.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, end, 0);
content.setSpan(new StyleSpan(android.graphics.Typeface.NORMAL), end, content.length(), 0);
textView.setText( content );
}
}
});
}
Try this way.You can apply multiple style this way.
You can also get width of textview by:
for (int i = 0; i < layout.getLineCount(); i++) {
maxLineWidth = Math.max(maxLineWidth, layout.getLineWidth(i));
}
I'm going to display a complete text document on several pages in an android app. Each page contains a TextView control which is in charge to display text. It also contains a next button, which is in charge to update textview and follow the text just from where it ended in last displayed section.
The problem is how can i find the last displayed word in displayable area of TextView?
Note: In this app, textView is not supposed to be scrolled or use ellipsize.
It also must be noted textsize can be increased or decreased.
To understand the situation better:
Sample Text to be displayed:
This is whole text. At first Page It just ended here and must be
followed from here.
And this is the sample illustration of the its display. The question is how to find word "here" as last word displayed in page 1?
I can't try this idea now but let's give it a shot! According to this post you can use the following method to find out if a string fits in a textview.
private boolean isTooLarge (TextView text, String newText) {
float textWidth = text.getPaint().measureText(newText);
return (textWidth >= text.getMeasuredWidth ());
}
So one idea, if the text is always the same, you can define the initial values for each text view manually and then when you increase or decrease the font you recalculate it, removing or adding words.
If it's not an option to input the initial values manually you can do something like:
String wordsInTextview = new String();
int lastUsedWord= 0;
int totalNumberOfWords = text.size();
for (int i=lastUsedWord;i<totalNumberOfWords;i++) {
if !(isTooLarge(TextView,wordsInTextview)) {
wordsInTextview = wordsInTextview + text(i); // add the words to be shown
} else {
TextView.setText(wordsInTextView);
lastUsedWord= i;
wordsInTextView = new String(); // no idea if this will erase the textView but shouldn't be hard to fix
}
}
You also would need to store the position of the first word of the textView, so when you resize the text you know where to start from!
int firstWordOnTextView = TextView.getText().toString().split(" ")[0]
And when it resizes you use it in the same method to calculate the text on the screen.
lastUsedWord = firstWordOnTextView;
If you want to be even faster, you can keep a track of how many words you have on each page, make an average and after a few runs always stat your loop from there. Or a few words before to avoid having to iterate back.
I believe this a reasonable solution if you don't have to display too many pages at once!
Sorry for mistakes in the code I don't have where to try it now! Any comments about this solution? Very interesting question!
Finally I developed piece of code which can find last displayed word.
Considering that we are going to use a text view with properties width and height as its size and textSize in dp as size of text, function formatStringToFitInTextView evaluates the last displayed word in the textview and returns it to as function output.
In below sample we considered SERIF font as TextView typeface.
String formatStringToFitInTextView(int width, int heigth, String inputText,float textSize) {
String[] words;
String lastWord = "";
String finalString="";
StaticLayout stLayout;
int i,numberOfWords;
int h;
Typeface tf = Typeface.SERIF;
TextPaint tp = new TextPaint();
tp.setTypeface(tf );
tp.setTextSize(textSize); //COMPLEX_UNIT_DP
words = inputText.split(" "); //split input text to words
numberOfWords = words.length;
for (i=0;i<numberOfWords;i++){
stLayout= measure(tp,finalString+words[i]+" ",width);
h=stLayout.getHeight();
if (stLayout.getHeight() > heigth) break;
finalString += words[i]+" ";
lastWord = words[i];
}
return lastWord;
}
StaticLayout measure( TextPaint textPaint, String text, Integer wrapWidth ) {
int boundedWidth = Integer.MAX_VALUE;
if( wrapWidth != null && wrapWidth > 0 ) {
boundedWidth = wrapWidth;
}
StaticLayout layout = new StaticLayout( text, textPaint, boundedWidth, Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false );
return layout;
}
The total logic to do actions is split the input text into words and add words one by one to textview till it fills up the provided height and width and then return the last added word as last displayed word.
Note: Thanks to Moritz, I used function measure from this answer.