I have a long text so I've decided to divide it into pages, and I put a so each swipe scrolls to the next page.. The way I did it was:
NumberOfPages=text.length()/CharPerPage; //CharPerPage=500;
NumberOfPages++;
Chapters.add(text.indexOf(" ", CurrentPage*CharPerPage));
CurrentPage++;
int tmp=0;
for(int i =NumberOfPages;i>0;i--)
{
tmp =(CurrentPage*CharPerPage);
if(tmp>text.length())
{
Chapters.add(text.length());
break;
}
else
{
Chapters.add(text.indexOf(" ", tmp));
CurrentPage++;
}
}
So I divide the text into pages, and each page has 500 chars... But this isnt good since Android has different screen sizes and shapes, and line breaks arent counted so it may go beyond the screen...
So can anyone suggest a way to know how many chars are needed to fill the screen so i can make the rest of the text to another page? Thanks.
Ok here is a shot - and a rather clumsy one but in short:
*You need need to know if any give line of text will fit width-wise in your view
*You need to know how many lines you have
*You need to handle embedded newlines
so
will some text fit on any given line
private boolean isTooLarge (TextView text, String newText) {
float textWidth = text.getPaint().measureText(newText);
return (textWidth >= text.getMeasuredWidth ());
}
how many lines does your textview have:
numLinesPerPage=mTextView.getHeight()/mTextView.getLineHeight(); //not this doesn't handle special cases where you've changed the font, bolded it etc
With these two tools you could iterate through your text adding words keeping track of how many lines you have left to work with (and handle your newlines if your text contains them)
note: getHeight can't be called in constructor since it doesn't know the height yet - you could also do this in onDraw or maybe onMeasure if you have a custom control.
Related
It seems that every angle I manage to find doesn't end up working in the way I need it to. My goal is to be able to customize the positioning and size of any scrollbar on any view, be it a recyclerview, gridview, or listview. I've tried using layer-list xmls to adjust the height and positioning, a Seekbar turned vertically, as well as trying to create my own scrollbar thumb and track using imageviews.
In terms of the layer-list, it just didn't have an effect on the scrollbar at all. The other two attempts at a solution (using a Seekbar, using individual imageviews) were nearly effective, except I needed the current scrolled position (getScrollY()) to be able to make the scrollbars I made actually accurate instead of just visually being a scrollbar. However, even though getScrollY() is defined for recyclerview, gridview and more, it always returns a 0, so I am unable to get that information (except for scrollviews, perhaps; I believe that's the only view type that properly returns a getScrollY() value).
Is it even possible to customize the scrollbar in this manner? I'd be keen to see references or documentation that can point me in the right direction. It feels like this is generally a non-issue for most developers on Android, or at least in general isn't something many people have asked for.
Edit
To assist in visualizing what I have and what I desire, here's a screenshot of the scrollbar as it is right now:
The following image is marked up to show what my intended outcome for this scrollbar would be:
Views have the capability for a scrollbar but a lot don't show them by default.
So any View has a whole load of XML attributes to customise the appearance, size and position.
But these are useless if not shown.
A lot of ViewGroups sub classes setWillNotDraw to be true and this removes the capability to draw the built in scrollbars of the View.
So to get any view to show it's built in scrollbars you need to the setWillNotDraw(false)
Getting any View to show it's built in scrollbars is Step 1 but again not all Views Calculate automatically the length and position of scroll hence they return 0 for the scroll position.
The View has to implement the following methods and return the appropriate numbers for the scroll position to be correct and things like getScrollY to return more than 0
// Length of scrollbar track
#Override
protected int computeHorizontalScrollRange() {
return (int) value;
}
// Position from thumb from the left of view
#Override
protected int computeHorizontalScrollOffset() {
return (int) value;
}
#Override
protected int computeVerticalScrollRange() {
return (int) value;
}
#Override
protected int computeVerticalScrollOffset() {
return (int) value;
}
Off Course some View sub classes don't use the built in ones but draw there own.
What I'm trying to achieve:
--- Stack Layout / Relative Layout---
- some widget (e.g. Label) -
- some widget (e.g. Label) -
- ListView -
-------------------------------------
However, I also want the following scroll behaviour:
Top widgets disappear first then ListView starts scrolling. Basically, I want a "natural" scrolling behaviour.
One way I can achieve this is by making the whole page a ListView and putting the widgets as a Header for the ListView
But that has one problem... which I think is a bug in Xamarin.Forms:
If you have a long Label (what else to hold text?), it will not display all of it. It will actually make it scrollable and display only part of it at a time. What makes this even worse is that you cannot scroll the Label "easily", you have to try multiple times to make it scroll the label instead of the page, it's obviously bugged. That happens even if the page itself has hit the end (i.e. can't scroll any more), the Label still can't be scrolled easily.
Is there another way or a workaround to achieve what I want?
As one of the comments suggests, the best is to set the HeightRequest of the Label to the needed value.
Here is how I measure the height of the text on Android (you'll need DependencyService, if you want to call this function from Xamarin.Forms):
double measureString(string text, string font, double fontSize, double width)
{
var textView = new TextView(global::Android.App.Application.Context);
textView.Typeface = Android.Graphics.Typeface.Create(font, Android.Graphics.TypefaceStyle.Normal);
textView.SetText(text, TextView.BufferType.Normal);
textView.SetTextSize(Android.Util.ComplexUnitType.Px, (float)(fontSize * Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.ScaledDensity));
int widthMeasureSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec((int)(width * Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.Density), width == 0 ? Android.Views.MeasureSpecMode.Unspecified : Android.Views.MeasureSpecMode.Exactly);
int heightMeasureSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec(0, Android.Views.MeasureSpecMode.Unspecified);
textView.Measure(widthMeasureSpec, heightMeasureSpec);
textView.SetIncludeFontPadding(false);
return (width == 0 ? textView.MeasuredWidth : textView.MeasuredHeight) / Xamarin.Forms.Forms.Context.Resources.DisplayMetrics.Density;
}
I have a TextView, and i want to know, at runtime, if the text is to long.
The requirement that is making this tricky, and thus haven't found a solution to it, is that I DO NOT want to use ellipsize, because I do NOT want to show three dots at the end.
ideas?
thanks
to check on runtime you have to use a paint object.
doc ref: Paint.measureText(String) and TextView.getPaint()
// the code below must be run AFTER the TextView have been layout on the screen.
Paint p = textView.getPaint();
float width = p.measureText("your text here");
if(width > textView.getWidth()){
// bigger
}
TextUtils class also have some interesting methods that you might be interested, for example, the method that calculates the ellipsize: TextUtils.ellipsize(...)
I dynamically add textviews to a relative layout based on user response to create a coloured grid pattern. Typically this can contain 5000+ textviews which have different background colors based on the value in textview tag.
I have this method where I iterate through all the textviews to show only those that have the same color and set the rest to gray. This works well when there are say 500 textviews but when the number is higher, say 5000 it take 13 seconds to complete.
if (code.equals("all")) {
for (int i = 0; i < textViewIDs.size(); i++) {
TextView tv = (TextView) findViewById(textViewIDs.get(i));
if (!tv.getTag().toString().equals("header")) {
tv.setBackgroundColor(Color.parseColor("#" + tv.getTag().toString()));
}
}
} else {
for (int i = 0; i < textViewIDs.size(); i++) {
TextView tv = (TextView) findViewById(textViewIDs.get(i));
if (!tv.getTag().equals(code)) {
if (!tv.getTag().toString().equals("header")) {
tv.setBackgroundColor(Color.LTGRAY);
}
}else{
tv.setBackgroundColor(Color.parseColor("#" + tv.getTag().toString()));
}
}
}
textViewIDs is the array holding all the textview ids.
What if anything can be done to speed this up?
Update:
I understand that having that number of widgets is not ideal however I could not come up with a better solution.
As well as each grid cells, in this case each texview, having different colors I also need to be able to manage onclick event for the cell so that I can add text. That was the reasoning for the textviews. Prior to using textviews I drew all the elements but that's when I couldn't find a way to add onclick event to each cell.
I'd better detail the concept to help you guys with what I'm trying to achieve and if I've gone down the wrong road.
This is part of a much larger app where I'm converting images into stitch charts.
Based on user input a grid of colored cells is drawn where each cell is a solid color that has been calculated from the original image most dominant color.
The grid will be larger than the screen so my views are placed in both horizontal and scroll views so they can be panned and zoomed. (This is all working well).
The grid cells have to click-able so I can turn on or off the background colors and also add a single text "X" character to mark stitch (cell) as completed.(This is to slow when the number of textview (cells) are > 500)
Hope there is enough detail there...
findViewById() seems to be your pressure point.
Instead of keeping a list of the ids, I'd a keep a list of references to the Views themselves (WeakReferences if leaks are a possibility)!
1 - for (int i = 0; i < textViewIDs.size(); i++) { ...
It's not optimized: precalculate your limit in a variable before starting the cycle:
int len = textViewIDs.size(); and use len in your cycle.
2 - i-- (I call it "reverse loop") seems to be faster than i++. See a nice loop comparison here
It's a bad practice to have that many TextView's, low-end devices will not be able to load it.
Try making one TextView with multiple styles in it, you can use HTML tags for background colors.
Or even better is to create just one ListView, this will recycle the views for you.
I want to create a text reader, which will cut the text and put it in the TextView (represents my pages) and one after the other. These are contained in a ViewFlipper (represents my entire text), which allows me to turn the pages.
By cons I can not cut my text intelligently. For now I limit each TextView to a number of characters, but it is very unstable. Indeed some letters take up more space than others .. We can not use this method.
How can we know what quantity of text will enter into a TextView?
I tried to find the maximum size of my View manually .
I invoked in:
float test = view.getPaint().measureText(myString);
It works well. When I put 480 letters (0 and 1), I get to 14,400 and my text is entirely within the screen. With:
1 -> 12.0
0 -> 12.0
é -> 11.0
\n -> 13.0
\r -> 7.0
total to fill the page -> 14400
The problem is that the line breaks and carriage return are causing problems.
If I have only letters or numbers in my string, so good: if we respect the maximum size of 14,400, everything is displayed on the screen. No Problem!
By cons, if a line breaks or a carriage return, it does not work. Even if we we respect the limit of 14400, the text above and is not fully displayed.
This is because the "MeasureText" method;" only returns 13 for a line break. But it's weird, a line break should account for at least 480 to make it consistent.
How do publishers eBook (Moon Reader, + Aldiko) for the text to be formatted so good? Each page has the same height, the text ends in the same place ..
You can fix the number of lines in your TextView by adding android:maxLines="some_integer". The other way is this:
if (myTextView.getMeasuredWidth() < myTextView.getPaint().measureText(myText)) {
myTextView.setHorizontalFadingEdgeEnabled(true);
myTextView.setHorizontallyScrolling(true);
}
But this will add cloudy effect to the last character if the text is too long.
Before get measured width or height call below method :
anyview.measure(0,0);
without calling this method you all wage getting 0 value for height and width