cant find size of text in setText method in textview - android

I have a custom class that extends TextView that replaces the last character with a custom bitmap emoji. For some reason the emoji is always much smaller than the text. here is the overridden method
#Override
public void setText(CharSequence text, BufferType type) {
super.setText("H", type);
SpannableString span = new SpannableString(text);
Bitmap emoji = BitmapFactory.decodeResource(getResources(), R.drawable.emoji);
int size = (int) (-this.getPaint().ascent());
Bitmap scaledEmoji = Bitmap.createScaledBitmap(emoji, size, size, true);
emoji.recycle();
ImageSpan ispan = new ImageSpan(c, scaledEmoji, ImageSpan.ALIGN_BASELINE);
span.setSpan(ispan, text.length()-1, text.length(), 0);
}
super.setText(span, type);
}
Edit: ok so the problem is the setText method is being called before the constructor. I don't get the issue if I set the text pragmatically rather than in the xml. But I would like to know if there is a fix for that and how it's even possible to call a method on an object before instantiating it.

try this
ImageSpan ispan = new ImageSpan(c, scaledEmoji,ImageSpan.ALIGN_BASELINE);
spannable.setSpan(ispan, getText().length()-1,
getText().length() + emoji.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

Related

Format first line of a multiline TextView in Android

I have multiline TextView, I need to format only single line of it. Since font size and style is dynamic I need it to format "first line" automatically.
This TextView is displaying a string without linefeed, it is wrapped automatically, I need to format first wrapped line.
Please check attached image:
I found the solution that uses StaticLayout.
private TextView tvTitle; //bold title with maxLines = 1
private TextView tvText; //the rest of the text
//initialize views, etc...
private void showText(#Nonnull String text) {
tvTitle.setText(text);
tvTitle.post(() -> {
String ellipsizedText = getEllipsizedText(text, tvTitle);
tvText.setText(ellipsizedText);
});
}
private static String getEllipsizedText(String text, TextView textView) {
StaticLayout staticLayout = getStaticLayout(text,
textView.getPaint(),
textView.getWidth(),
1,
null);
return text.substring(staticLayout.getLineEnd(0));
}
private static StaticLayout getStaticLayout(CharSequence text,
TextPaint paint,
int width,
int maxLines,
TextUtils.TruncateAt ellipsize) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return StaticLayout.Builder.obtain(text, 0, text.length(), paint, width)
.setTextDirection(TextDirectionHeuristics.FIRSTSTRONG_LTR)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0f, 1f)
.setIncludePad(false)
.setEllipsize(ellipsize)
.setEllipsizedWidth(width)
.setMaxLines(maxLines)
.build();
} else {
return new StaticLayout(text,
paint,
width,
Layout.Alignment.ALIGN_NORMAL,
1f,
0f,
false);
}
}
from #HiteshGehlot's answer:
myTextView.setText(Html.fromHtml("<b>This is a normal</b><br>second line<br>third line"));
but as your system is dynamic, this is not a good way to do it. This code generates the appropriate HTML:
String lines[] = string.split("\\n");
String fl = String.format(Locale.ENGLISH, "<b>%s</b><br>", lines[0]);
StringBuilder sb = new StringBuilder();
sb.append(fl);
for(int i = 1; i < lines.length; i++){
sb.append(lines[i] + "<br>");
}
String finalProduct = sb.toString();
then:
myTextView.setText(Html.fromHtml(finalProduct));
This should dynamically create the HTML tags for the first line
EXPLANATION
This is an improvement from a previous answer. Using HTML, the first line is formatted as bold.
Using regex, we split all the lines by newline(\n). The first String in the array is the first line, while the rest aren't important to format. So first we append the first line, and add the rest after that. The for-loop starts at 1 since 0 is the first line.
NOTE
If you do break the text dynamically, you pass that String into the method above. The method I have presented here uses those linebreaks to figure out which line is where, to be able to set the first line to be bold
try this:
myTextView.setText(Html.fromHtml("<b>This is a normal</b><br>second line<br>third line"));
Just use another TextView.
TextView1, TextView2, TextView3.
And set TextView1 to bold.

ImageSpan Size Measurement with TextView and StaticLayout

I have a simple layout contains just one TextView.
I wanna load an image into TextView using ImageSpan.
Below method creates Spannable:
private Spannable getImageSpannable(int drawableId, int targetWidth, int targetHeight) {
Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), drawableId);
Bitmap bitmap = Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, true);
Drawable dr = new BitmapDrawable(getResources(), bitmap);
dr.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
Spannable imageSpannable = new SpannableString("\uFFFC");
ImageSpan imgSpan = new ImageSpan(dr, DynamicDrawableSpan.ALIGN_BOTTOM);
imageSpannable.setSpan(imgSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return imageSpannable;
}
I use this method to create content like this:
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, 100, 260));
content.append("\n");
txtContent.setText(content);
}
When I call setContent() method my result is something like this:
As you see there is small gap between ImageSpan and top of TextView.
This is not line spacing, because I set line spacing to 0.
And interesting point is when I remove "\n" from content(declared in setContent method) this space is gone.
And another point is that when I tried to measure content size using StaticLayout, with "\n" at the end bottom of line 0 it returns 270 and without "\n" it returns 260.
This behavior causes some difficulties for me, because I have to measure text and ImageSpan using StaticLayout and decide witch one can fit into TextView.
I appreciate everyone can help me.
Thanks.
Here's my xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/txtContent"
android:layout_width="300dp"
android:layout_height="500dp"
android:background="#fed9f4"
android:textSize="22sp"/>
</LinearLayout>
I'v done some tests and I find that font size is affects ImageSpan rendering.
Can somebody explain this affect please?
I hope this method works
The following line of code
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, 100, 260));
content.append("\n");
txtContent.setText(content);
}
Change to
public void setContent() {
SpannableStringBuilder content = new SpannableStringBuilder();
content.append(getImageSpannable(R.drawable.my_image, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
content.append("\n");
txtContent.setText(content);
}
And resize "R.drawable.my_image" dimensions

Custom item list view with Image View after the last character of textview

I want custom item list view same image
what can i do to create item list view with small image view just like in the picture
Thank you very much
It looks like you actually want the image to be inline with the text.
In this case, you wouldn't use an ImageView. You would create a Spannable that contains an ImageSpan and assign it to the TextView.
String str = "Thank you very much [icon]";
int start = str.indexOf("[icon]");
int end = start + "[icon]".length();
SpannableString ss = new SpannableString(str);
Drawable d = getResources().getDrawable(R.drawable.headphones);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d);
ss.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(ss);
Use a textView, create html text with image tag, use Html.fromHtml. Read the docs on fromHtml, as you will still have to replace the images.
Alternatively, you can use a WebView, though I don't like the overhead.

Android: How to place a button end of a textview with multiline text?

I want to place a button at end of the textview paragraph, like as "Go" button that when user click on it the app going to another page.
for example:
if you have good endurance,
for killing the monster you must
going to section 2. [Go->]
-if you haven't good endurance,
flee to section 3. [Go->]
in above example [Go->] is a tiny button that must placing exactly in end of line.
how I can do it in runtime?
You can use spans for this.
Let's assume you have a TextView called myText.
Drawable goButtonDrawable = getResources().getDrawable(R.drawable.go_button);
String text = "If you have good endurance, for killing the monster you must go to section 2. [GO]"
String replace = "[GO]";
final int index = text.indexOf(replace);
final int endIndex = index + replace.length();
final ImageSpan imageSpan = new ImageSpan(goButtonDrawable, ImageSpan.ALIGN_BASELINE);
final ClickableSpan clickSpan = new ClickableSpan() {
#Override public void onClick(View clicked) {
// Do your [GO] action
}
};
SpannableString spannedText = new SpannableString(text);
spannedText.setSpan(imageSpan, index, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannedText.setSpan(clickSpan, index, endIndex , Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
myText.setText(spannedText);
Obviously this could be better abstracted (you could just make a custom TextView that handles this internally), but that's the general idea.
You can achieve this by setting the text as html
Append the HTML img tag to your text and set it to text view like this
String htmlText = "Your multi line text <img src=\"ic_go_icon\">";
textView.setText(Html.fromHtml(htmlText, new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
int resourceId = getResources().getIdentifier(source, "drawable",getPackageName());
Drawable drawable = getResources().getDrawable(resourceId);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
}, null));
But handling the click will be for complete text view.

Change the color of the underline in android

I am developing the android application. I need to underline some of the Textview.
SpannableString content = new SpannableString("Ack:");
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
tvAck.setText(content);`
I have used the above code for that. But now i want to change the color of the underline. Can any one tell me how to do so. Any help or suggestion is accepted.
There is no documented method to set the underline color. However, there is an undocumented TextPaint.setUnderline(int, float) method which allows you do provide the underline color and thickness:
final class ColoredUnderlineSpan extends CharacterStyle
implements UpdateAppearance {
private final int mColor;
public ColoredUnderlineSpan(final int color) {
mColor = color;
}
#Override
public void updateDrawState(final TextPaint tp) {
try {
final Method method = TextPaint.class.getMethod("setUnderlineText",
Integer.TYPE,
Float.TYPE);
method.invoke(tp, mColor, 1.0f);
} catch (final Exception e) {
tp.setUnderlineText(true);
}
}
}
I haven't tried this myself, so this is more an idea than a solution, but probably worth trying. Class UnderlineSpan has method updateDrawState, which takes TextPaint as a parameter. In turn, TextPain can has field public int linkColor.
So for you it would be something like
TextPaint tp = new TextPaint();
tp.linkColor = [your color]; //not quite sure what the format should be
UnderlineSpan us = new UnderlineSpan();
us.updateDrawState(tp);
SpannableString content = new SpannableString("Ack:");
content.setSpan(us, 0, content.length(), 0); tvAck.setText(content);
Reference for both TextPaint and UnderlineSpan are very poor, with majority of javadoc missing altogether (judge for yourself: http://developer.android.com/reference/android/text/TextPaint.html), so I'm not certain how to use these though.
In TextPaint, there has a field 'underlineColor' and method 'setUnderlineText', indicated and can use to changed the underline color. But, they are '#hide' field and method, to use them, you must using reflecting, like this:
Field field = TextPaint.class.getDeclaredField("underlineColor");
field.setAccessible(true);
field.set(ds, mUnderlineColor);
ds is your TextPaint Object.
Really late to encounter this scenrio. Here's another way, It would be to set multiple spans to the same spannable content:
SpannableString content = new SpannableString("Ack:");
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.red)),
0,
content.length(),
0
);
tvAck.setText(content, TextView.BufferType.SPANNABLE);

Categories

Resources