I have a span like this:
private SpannableStringBuilder spandex(List<String> ret, Boolean startDark) {
SpannableStringBuilder spanBuilder = new SpannableStringBuilder();
Spannable span = null;
int color = 0;
int startDarkMod = 0;
if(startDark)
startDarkMod = 1;
for (String x : ret) {
if ((ret.indexOf(x) + startDarkMod) % 2 > 0)
color = WzTheme.NOT_HIGHLIGHTED_COLOR;
else
color = WzTheme.ALT_NOT_HIGHLIGHTED_COLOR;
span = new SpannableString(x.toUpperCase());
span.setSpan(new ForegroundColorSpan(color),
0,
x.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanBuilder.append(span);
}
return spanBuilder;
}
public static int NOT_HIGHLIGHTED_COLOR = Color.rgb(68, 68, 68);
public static int ALT_NOT_HIGHLIGHTED_COLOR = Color.rgb(110, 110, 110);
It produces light gray and dark grey code like this:
Then there is that bright white "T". It happens after every ß character, which, as the other thread mentioned, gets converted to SS when capitalized. I would prefer no capatalization of that char, but I can live with the conversion to SS. What I need to stop from happening is the big white "T". Any ideas?
ugh, this fixes it:
private String myUpperCase(String word) {
word = word.replaceAll("ß", "XXX");
return word.toUpperCase().replaceAll("XXX", "ß");
}
so as you probably already know, the issue is that ß gets expanded to SS in java utf-8 toUpperCase() and that increases the length of the string in the span by one, but "x" (in my code above) is one char short (length) so i get the default white text.
i am happy with the fix.
Related
I want to set the minimum fixed width for an EditText so that it can contain its hint but also the typed, length-limited content like a number of 2 digits.
Some details:
I want to be able to do this dynamically since I have numerous
fields for different purposes with different hints (in different languages) and input length (some 2 digits, others 4).
Hints are not necessarily longer than the input itself. A
hint could be "dd" or "Day" and the input could be a to digit
number.
I do not need room for hint and content at the same time;
hints disappear when the user starts typing.
I'm using custom fonts in an extended EditText class, but that should be handled as I'm copying the EditText's Paint.
I have a utility method for doing so, but it returns a width that is too narrow so the hint is clipped. What am I doing wrong?
The EditText is specified in XML like this:
<EditText
android:id="#+id/birthday_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:hint="#string/birthday_month_hint"
android:lines="1"
android:maxLength="2">
In my Activity I first find the EditText and then prepare it using Texts.setNiceAndTightWidth(monthEditText, 2) defined below (including helper methods):
public class Texts
{
public static void setNiceAndTightWidth ( EditText editText, int maxInputLength )
{
// String of chars to test for widest char. Include all possible input chars and chars of hint, as we need to make room for hint as well.
String testChars = String.format("1234568790%s", editText.getHint().toString());
char widestChar = getWidestChar(editText, testChars);
String widestString = repeat(widestChar, maxInputLength);
float widestStringWidth = getTextWidth(editText, widestString);
int width = (int)(widestStringWidth + 0.5f);
editText.setWidth(width);
// This was an experiment but it doesn't help.
ViewGroup.LayoutParams lp = editText.getLayoutParams();
lp.width = width;
editText.setLayoutParams(lp);
}
public static char getWidestChar ( TextView textView, String testChars )
{
float width, widest = 0;
char widestChar = '\0';
// Using Paint properties of TextView, including Fontface, text size, etc.
Paint paint = new Paint( textView.getPaint() );
for ( int i = 0 ; i < testChars.length() ; i++ ) {
width = paint.measureText(testChars, i, i+1);
if ( width > widest ) {
widest = width;
widestChar = testChars.charAt(i);
}
}
return widestChar;
}
public static String repeat ( char ch, int length )
{
char[] chars = new char[length];
Arrays.fill(chars, ch);
String string = String.valueOf(chars);
return string;
}
public static float getTextWidth ( TextView textView, CharSequence text )
{
Paint paint = new Paint( textView.getPaint() );
float width = paint.measureText(text, 0, text.length());
return width;
}
}
How to set span for special characters, like \n \t \r , etc ? Right now if i do this:
getText().setSpan(DynamicListView.mBackgroundColor, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Thanks
Not 100% sure I understand the question, as the special characters '\n' nd '\r' are line breaks, so how could they be styled as they will be invisible?
That said, here is a method for styling certain chars in an EditText. You can add as many chars as you like to the end of the method and any instances of those characters will be styled.
//...
editText.setText(getSpannedText(editText.getText(), `u`, `r`));
//...
private static SpannableString getSpannedText(String text, char... triggers) {
SpannableString spanString = new SpannableString(text);
for (int i = 0; i < spanString.length(); i++) {
for (char trigger : triggers) {
if (spanString.charAt(i) == trigger) {
spanString.setSpan(new ForegroundColorSpan(Color.CYAN), i, i+1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
return spanString;
}
Fixed on my side by Draw functions, here is code of EditText:
if(getText().toString().substring(start,end).indexOf("\t")>=0
|| getText().toString().substring(start,end).indexOf("\n")>=0
|| getText().toString().substring(start,end).indexOf("\r")>=0) {
TextPaint paint = new TextPaint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(mBackgroundColor);
paint.bgColor = mBackgroundColor;
Layout layout = getLayout();
int line = layout.getLineForOffset(start);
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
float x = layout.getPrimaryHorizontal(start);
float y = baseline + ascent;
Rect rect = new Rect();
//rect.set(start, 0, Math.round(layout.getSecondaryHorizontal(end)), getLineHeight()+5);
rect.set(start, 0, Math.round(layout.getSecondaryHorizontal(end)), getLineHeight()+5);
rect.offset(Math.round(x), Math.round(y));
Log.d("debug","tabs " + rect.toString());
canvas.drawRect(rect, paint);
}
I am trying to apply Bulleted list and Numbering list in android edit-text when user press button. For that i have tried below code.
For Bullet
BulletSpan[] quoteSpan = str.getSpans(selectionStart, selectionEnd, BulletSpan.class);
// If the selected text-part already has UNDERLINE style on it, then we need to disable it
for (int i = 0; i < quoteSpan.length; i++) {
str.removeSpan(quoteSpan[i]);
exists = true;
}
// Else we set UNDERLINE style on it
if (!exists) {
str.setSpan(new BulletSpan(10), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
and for Numbering
int number = 0;
NumberIndentSpan[] quoteSpan1 = str.getSpans(selectionStart, selectionEnd, NumberIndentSpan.class);
number ++;
// If the selected text-part already has UNDERLINE style on it, then we need to disable it
for (int i = 0; i < quoteSpan1.length; i++) {
str.removeSpan(quoteSpan1[i]);
exists = true;
}
if (!exists){
str.setSpan(new NumberIndentSpan(5, 15, number), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
NumberIndentSpan.java
public class NumberIndentSpan implements LeadingMarginSpan {
private final int gapWidth;
private final int leadWidth;
private final int index;
public NumberIndentSpan(int leadGap, int gapWidth, int index) {
this.leadWidth = leadGap;
this.gapWidth = gapWidth;
this.index = index;
}
public int getLeadingMargin(boolean first) {
return leadWidth + gapWidth;
}
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 l) {
if (first) {
Paint.Style orgStyle = p.getStyle();
p.setStyle(Paint.Style.FILL);
float width = p.measureText("4.");
c.drawText(index + ")", (leadWidth + x - width / 2) * dir, bottom
- p.descent(), p);
p.setStyle(orgStyle);
}
}
}
Through above code i am able to add Bullet and Number at particular position but the problem is, i want to implement functionality like, when i press enter key it should automatically add Bullet to next line and again if i press Enter key without entering any text it should remove that Bullet, if i enter some text on second line and then if i press enter key, it should add Bullet to next line also. Similar kind of functionality like this application.
https://play.google.com/store/apps/details?id=com.hly.notes
Same for number listing also, it is always adding 1) it is not incrementing the value, and for number listing also i want to implement similar king of functionality like bullet, when i apply number list and when i press enter it should automatically add 2) to next line.
Anyone have any idea how to achieve this. Thank you in advance.
I'm using indexof() to find the length of a specific character, then color it with different color. I can start the coloring exactly with the character using the indexof() which is great, but I can't end it to the word.
if(title.contains("x")){
SpannableString WordtoSpan = new SpannableString(title);
WordtoSpan.setSpan(new ForegroundColorSpan(Color.RED), title.indexOf("x"), title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
holder.txtTitle.setText(WordtoSpan);
} else {
holder.txtTitle.setText(title);
}
Now if it contains the character x it will color it with red from the first x it finds. But it won't end until the last of the string which I dont like.
I want it to end with the end of the word that started with an x.
ex. Now: (Bold is the color red atm)
I love x-man and chocolate.
What I want is:
I love x-man and chocolate.
how do I achieve that?
You must find where the word ends, not simply passing length();
Consider a more robust algorithm to find word separator (ie include tabs), in the example I simply find the first whitespace
String s = "I love x-man and chocolate.";
int idx = s.indexOf("x");
if (idx >= 0) {
int wordEnd = s.indexOf(" ", idx);
if (wordEnd < 0) {
wordEnd = s.length();
}
WordtoSpan.setSpan(new ForegroundColorSpan(Color.RED),
idx,
wordEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
So I have a TextView in android that has the width of the whole length of the screen and a padding of dip 5. How can I calculate the number of characters that will fit a single line on the screen? I guess in other words, I'm trying to get the number of columns of a textview?
I considered manual calculation depending on textsize and width, but 1) don't know the correlation and 2) due to the padding in the units of dip, different screens will use different number of actual pixels to pad.
Overall Question: I am trying to use this to solve: if given a string how can I manually edit to string such that when the textview prints the string character by character, I will know when to start a word that won't fit on one line on the next. Note: I know that textview automatically puts words that won't fit onto the next line, however, since I'm printing character by character, like typing animation, textview doesn't know the word won't fit until it prints out the overflowing characters of that word.
Been searching everywhere for this...
Thanks!
Added solutions:
one possible solution:
public String measure2 (TextView t, String s) {
String u = "";
int start = 0;
int end = 1;
int space = 0;
boolean ellipsized = false;
float fwidth = t.getMeasuredWidth();
for(;;) {
//t.setText(s.substring(start, end));
float twidth = t.getPaint().measureText(s.substring(start, end));
if (twidth < fwidth){
if (end < s.length())
end++;
else {
if (!ellipsized)
return s;
return u + s.subSequence(start, end);
}
}
else {
ellipsized = true;
space = (u + s.substring(start, end)).lastIndexOf(" ");
if (space == -1)
space = end - 1;
u += s.subSequence(start, space) + "\n";
start = space + 1;
end = start + 1;
}
}
}
solution 2, but still uses solution1 sometimes:
public String measure3 (TextView t, String s) {
List<String> wlist = Arrays.asList(s.split(" "));
if (wlist.size() == 1)
return measure2(t, s);
String u = "";
int end = 1;
float fwidth = t.getMeasuredWidth();
for(;;) {
//t.setText(s.substring(start, end));
if (wlist.isEmpty())
return u;
String temp = listStr(wlist, end);
float twidth = t.getPaint().measureText(temp);
if (twidth < fwidth){
if (end < wlist.size())
end++;
else {
return u + temp;
}
}
else {
temp = listStr(wlist, end-1);
if (end == 1)
temp = measure2(t, temp);
if (wlist.isEmpty())
return u + temp;
else
u = u + temp + "\n";
wlist = wlist.subList(end - 1, wlist.size());
end = 1;
}
}
}
public String listStr (List<String> arr, int end) {
String s = "";
for (String e : arr.subList(0, end) ){
s = s + e + " ";
}
return s.trim();
}
I used the above code to generate off a original string s, a string u that would be printed. However, I think this approach is very inefficient. Is there another approach or a better algorithm? Note: there are some errors in measure3 that I fixed, but was too lazy to edit
Try this:
private boolean isTooLarge (TextView text, String newText) {
float textWidth = text.getPaint().measureText(newText);
return (textWidth >= text.getMeasuredWidth ());
}
Detecting how many characters fit will be impossible due to the variable width of the characters. The above function will test if a particular string will fit or not in the TextView. The content of newText should be all the characters in a particular line. If true, then start a new line (and using a new string to pass as parameter).
Answer to the comment:
because the app can be run in many systems is exactly why you need to measure it.
This is a way to solve your "overall question". What is the difference between using str.size()>numCol vs is too large? You will need to implement your animation (hint #1: insert a newline character)
as I said before when you start a new line, you start a new string (hint #2: if you extend TextView, you can implement all this in overriding setText). (hint #3: Keep track of the lines created with a static int lines; and use newString.split("\\r?\\n")[lines-1] to check for length).
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.
I had the same issue and I calculated the number characters per line by 2 steps:
Step 1: Calculate the number of lines
val widthOfTvComment = widthOfScreen - marginLeft - marginRight
var bounds = Rect()
var paint = Paint()
paint.textSize = textSize
paint.getTextBounds(comment,0,comment.length,bounds)
val lines = ( bounds.width()/widthOfTvComment)
Step 2: Calculated the number characters per line
val charactersPerLine = comment.length / lines