Currently, I wish to add an image in between texts and align it to the top of the TextView.
Something like this:
The only vertical alignments I can find are baseline (which seems to put it right down the center of the text) and align bottom.
What happens if I use ALIGN_BASELINE is:
Is there a way to align it to the top instead?
My current code:
txtView.setText(this.addImageAsterisk(
"The string to have asterisk at the end*"), BufferType.SPANNABLE);
then
private CharSequence addImageAsterisk(String string) {
Drawable d = context.getResources().getDrawable(R.drawable.img_asterisk);
ImageSpan imageSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
final SpannableString spannableString = new SpannableString(string);
spannableString.setSpan(imageSpan, string.length()-1, string.length(), 0);
return spannableString;
}
removing ImageSpan.ALIGN_BASELINE sets it to align to the bottom which is also not my expected result.
--- Thank you user Lalit Poptani, I tried applying your answer----
after applying this, what happens is that the whole textview seems to have extra margin top.
before applying span:
This is the text*
after applying the SuperscriptSpanAdjuster
(some extra space)
This is the text*
My code:
String string = "This is the text*";
Drawable d = this.context.getResources().getDrawable(R.drawable.img_asterisk);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
SuperscriptSpanAdjuster s = new SuperscriptSpanAdjuster(1.5);
final SpannableString spannableString = new SpannableString(string);
spannableString.setSpan(s, string.length() - 1, string.length(), 0);
spannableString.setSpan(imageSpan, string.length(), string.length() + 1, 0);
textView.setText(spannableString);
What you can do is use a custom MetricAffectingSpan for maintaining its ratio like,
public class SuperscriptSpanAdjuster extends MetricAffectingSpan {
double ratio = 0.5;
public SuperscriptSpanAdjuster(double ratio) {
this.ratio = ratio;
}
#Override
public void updateDrawState(TextPaint paint) {
paint.baselineShift += (int) (paint.ascent() * ratio);
}
#Override
public void updateMeasureState(TextPaint paint) {
paint.baselineShift += (int) (paint.ascent() * ratio);
}
}
And the you can use SpannableString to apply asterisk to your String like,
SpannableString mString = new SpannableString("This is what I wanted*");
mString.setSpan(new SuperscriptSpanAdjuster(0.5), mString.length() - 1,
mString.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
mTextView.append("\n");
mTextView.append(mString);
This is append asterisk to your text as you required. And your output will be as,
Just wrap your drawable in inset drawable and set inset bottom to some value like 8dp. Not the best solution, but will work.
Modify your drawable/img_asterisk.xml --
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="8dp">
<!-- Your previous asterisk drawable, probably a 'shape' -->
</inset>
Related
I want to create a SpannableString with some emotions and text like this image.
Below is the method that I have used so for but it just attach emotions with text. Please suggest me how can I create such types of view.
private SpannableStringBuilder getLikeCountString(UserFeedData feedData, Context mContext) {
SpannableStringBuilder builder = new SpannableStringBuilder();
int emotionSize = (int) mContext.getResources().getDimension(R.dimen.emotion_size);
if (feedData.getEmotionTypes() != null) {
String[] emotions = feedData.getEmotionTypes().split(",");
for (String emotion : emotions) {
SpannableString emojSpan = new SpannableString(" ");
// Getting image based on emotion id
Drawable icon = ContextCompat.getDrawable(mContext, ContentDetailFragment.getLikeEmotionResource(Integer.valueOf(emotion.trim())));
//icon.setBounds(0, 0, (icon.getIntrinsicWidth() / 2) + 5, (icon.getIntrinsicHeight() / 2) + 5);
icon.setBounds(0, 0, emotionSize, emotionSize);
ImageSpan imageSpan = new ImageSpan(icon, ImageSpan.ALIGN_BOTTOM);
emojSpan.setSpan(imageSpan, 0, emojSpan.length() - 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
builder.append(emojSpan);
}
}
if (feedData.getContentLikes() > 0) {
builder.append(feedData.getContentLikes() + " ");
}
return builder;
}
Instead of using individual ImageSpans for each emoji, use one ImageSpan with LayerDrawable inside, one emoji per layer. You can do all kinds of overlaps with LayerDrawable.
I want to set the three different text size in single textview as like the below image sample,
I have tried with Html as like the below,
StringBuilder fontString = new StringBuilder();
fontString.append("<small>");
fontString.append("1.78");
fontString.append("</small>");
fontString.append("<big><b>");
fontString.append("33");
fontString.append("</b></big>");
fontString.append("<sup>");
fontString.append("<small>");
fontString .append("2");
fontString.append("</small>");
fontString.append("</sup>");
Also I tried SpannableString formatting,
SpannableString spanString = new SpannableString("1.34456");
spanString.setSpan(new RelativeSizeSpan(1.75f), 0,spanString.length()-2-1, 0);
spanString.setSpan(new StyleSpan(Typeface.BOLD), spanString.length()-2-1, spanString.length()-1, 0);
spanString.setSpan(new ForegroundColorSpan(Color.RED), spanString.length()-2-1, spanString.length()-1, 0);
spanString.setSpan(new RelativeSizeSpan(3f), spanString.length()-2-1, spanString.length()-1, 0);
spanString.setSpan(new RelativeSizeSpan(1.25f), spanString.length()-1, spanString.length(), 0);
like the below mentioned image,
Please suggest me if you know the solution,I have tried with superscript or subscript also but no luck.
You can use Html tags such as
<h1>...<h6>
using below code
txtview.setText(Html.fromHtml("your text here with HTML heading tags");
May this help you
You can use SpannableString with RelativeSizeSpan to create multiple text sizes in one textview. Set the gravity of the text to TOP so text will attached to the top edge.
Please find the solution,
Use this class to align the Text,
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;
public class SuperscriptSpanAdjuster extends MetricAffectingSpan {
double ratio = 0.5;
public SuperscriptSpanAdjuster() {
}
public SuperscriptSpanAdjuster(double ratio) {
this.ratio = ratio;
}
#Override
public void updateDrawState(TextPaint paint) {
paint.baselineShift += (int) (paint.ascent() * ratio);
}
#Override
public void updateMeasureState(TextPaint paint) {
paint.baselineShift += (int) (paint.ascent() * ratio);
}
}
Here, you can find the SpannedString format,
SpannableString spanString = new SpannableString(value);
/* To show the text in top aligned(Normal)*/
spanString.setSpan(new SuperscriptSpanAdjuster(0.7), 0,spanString.length()-mostSignificantLength-leastSignificantLength, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
/* Show the number of characters is normal size (Normal)*/
spanString.setSpan(new RelativeSizeSpan(1.3f), 0,spanString.length()-mostSignificantLength-leastSignificantLength, 0);
/*To set the text style as bold(MostSignificant)*/
spanString.setSpan(new StyleSpan(Typeface.BOLD), spanString.length()-mostSignificantLength-leastSignificantLength, spanString.length()-leastSignificantLength, 0);
/*To set the text color as WHITE(MostSignificant)*/
spanString.setSpan(new ForegroundColorSpan(Color.WHITE), spanString.length()-mostSignificantLength-leastSignificantLength, spanString.length()-leastSignificantLength, 0);
/*Show the number of characters as most significant value(MostSignificant)*/
spanString.setSpan(new RelativeSizeSpan(2.3f), spanString.length()-mostSignificantLength-leastSignificantLength, spanString.length()-leastSignificantLength, 0);
/* To show the text in top aligned(LestSignificant)*/
spanString.setSpan(new SuperscriptSpanAdjuster(1.2), spanString.length()-leastSignificantLength, spanString.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
/*To set the text style as bold(LestSignificant)*/
spanString.setSpan(new StyleSpan(Typeface.BOLD), spanString.length()-leastSignificantLength, spanString.length(), 0);
/*Show the number of characters as most significant value(LestSignificant)*/
spanString.setSpan(new RelativeSizeSpan(0.8f), spanString.length()-leastSignificantLength, spanString.length(), 0);
Happy coding...
you use method Html.fromhtml("your html formatted text") It worked for me, thanks.
By following this question, I was able to have text around an image. However, I have the following problem.
As you can see, the space for the image on top is displayed in every paragraph at the right. In the question someone had this problem and suggested to change 'ss.length()' for 'lines'. This seemed to work except if the first paragraph was too short, the next paragraph would overlap the image.
I modified the FlowTextHelper class slightly to use text from Html. This is the code I'm using:
public class FlowTextHelper {
private static boolean mNewClassAvailable;
/* class initialization fails when this throws an exception */
static {
try {
Class.forName("android.text.style.LeadingMarginSpan$LeadingMarginSpan2");
mNewClassAvailable = true;
} catch (Exception ex) {
mNewClassAvailable = false;
}
}
public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display, int addPadding){
// There is nothing I can do for older versions, so just return
if(!mNewClassAvailable) return;
// Get height and width of the image and height of the text line
thumbnailView.measure(display.getWidth(), display.getHeight());
int height = thumbnailView.getMeasuredHeight();
int width = thumbnailView.getMeasuredWidth() + addPadding;
messageView.measure(width, height); //to allow getTotalPaddingTop
int padding = messageView.getTotalPaddingTop();
float textLineHeight = messageView.getPaint().getTextSize();
// Set the span according to the number of lines and width of the image
int lines = (int)Math.round((height - padding) / textLineHeight);
//SpannableString ss = new SpannableString(text);
//For an html text you can use this line:
if(!text.equals("")) {
SpannableStringBuilder ss = (SpannableStringBuilder) Html.fromHtml(text);
ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);
messageView.setText(ss);
messageView.setMovementMethod(LinkMovementMethod.getInstance()); // links
// Align the text with the image by removing the rule that the text is to the right of the image
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) messageView.getLayoutParams();
int[] rules = params.getRules();
rules[RelativeLayout.RIGHT_OF] = 0;
}
}
}
public class MyLeadingMarginSpan2 implements LeadingMarginSpan.LeadingMarginSpan2 {
private int margin;
private int lines;
public MyLeadingMarginSpan2(int lines, int margin) {
this.margin = margin;
this.lines = lines;
}
#Override
public int getLeadingMargin(boolean first) {
return first ? margin : 0;
}
#Override
public int getLeadingMarginLineCount() {
return lines;
}
#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) {}
}
What is causing the space being repeated every paragraph and how can I get rid of it? Any help is appreciated.
I've spend hours to solve this issue, but solved it with thanks to the answer found here:
text wrapping around image in android
Basically as follows:
First add a margin to your textview and set the text
final RelativeLayout.LayoutParams params = RelativeLayout.LayoutParams)messageView.getLayoutParams();
params.setMargins(marginWidth, 0, 0, 0);
messageView.setText(Html.fromHtml(text));
Then add an OnGlobalLayoutListener and in the onGlobalLayout() call you calculate how many lines actually need the margin. You split the lines in 2 separate spannables and add the Margin only to the first one:
messageView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() {
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
int linesCount = messageView.getLayout().getLineCount();
// restore the margin
params.setMargins(0, 0, 0, 0);
SpannableString spanS = new SpannableString ( Html.fromHtml(text) );
if (linesCount <= lines) {
spanS.setSpan(new MyLeadingMarginSpan2(lines, width), 0, spanS.length(), 0);
messageView.setText(spanS);
} else {
// find the breakpoint where to break the String.
int breakpoint = messageView.getLayout().getLineEnd(lines-1);
Spannable s1 = new SpannableStringBuilder(spanS, 0, breakpoint);
s1.setSpan(new MyLeadingMarginSpan2(lines, width), 0, s1.length(), 0);
Spannable s2 = new SpannableStringBuilder(System.getProperty("line.separator"));
Spannable s3 = new SpannableStringBuilder(spanS, breakpoint, spanS.length());
// It is needed to set a zero-margin span on for the text under the image to prevent the space on the right!
s3.setSpan(new MyLeadingMarginSpan2(0, 0), 0, s3.length(), 0);
messageView.setText(TextUtils.concat(s1, s2, s3));
}
// remove the GlobalLayoutListener
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
messageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
messageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
If you need to wrap text around an image, use this library FlowTextView.
The library performs well, and it can be used with a couple lines. However, it does not support screen pixel size for fonts. I found a workaround with this answer, so that you can convert pixel size to sp.
I hope this helps anyone and you don't waste as much time as me using the question from my original post.
I am trying to create spannable textview and showing it in EditText. So user can type something in EditText and if user pressed enter button of keyboard then i am converting this text in to spannable textview after this user can start typing again and press enter button of keyboard then again second spannable textview will create ans will show it in edittext but.
Where i stuck?
when i create two spannable textview then this two textview slightly overlapping on each other. And i want to set margin between this two textview.
I also tried to set margin between textview using LayoutParam but not success.
Here is image which showing textview overlapping on each other in EditText.
y of spicy is hidden below tasty
Here is my code.
txtDishTags.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH
|| actionId == EditorInfo.IME_ACTION_NEXT
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
txtDishTags.dismissDropDown();
if(txtDishTags.getText().toString().trim().length()>=1){
isEntered = true;
String[] separated = tags.split(",");
tags = separated[separated.length-1];
if(tags.trim().length()>=1){
TextView tv = createContactTextView(tags);
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(-20, 0, bd.getIntrinsicWidth(),
bd.getIntrinsicHeight());
sb.append(tags + ",");
sb1 = new SpannableStringBuilder();
sb1.append(tags + ",");
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length(), sb.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(clickSpan, sb.length() - tags.length(),
sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
txtDishTags.setText("");
txtDishTags.setText(sb);
int length = sb.length();
txtDishTags.setSelection(length, length);
}
}
}
return false;
}
});
public TextView createContactTextView(String text) {
//llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 44);
//llp.setMargins(5, 0, 20, 0);
TextView tv = new TextView(this);
tv.setText(text);
tv.setTextSize(30);
Typeface faceBook = Typeface.createFromAsset(getAssets(),
"fonts/eau_sans_book.otf");
tv.setTypeface(faceBook);
tv.setTextColor(getResources().getColor(R.color.backgroundcolor));
tv.setBackgroundResource(R.color.textviewbubble);
//tv.setLayoutParams(llp);
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
44, r.getDisplayMetrics());
tv.setHeight(px);
return tv;
}
public static Object convertViewToDrawable(View view) {
int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
view.measure(spec, spec);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(),
view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
c.translate(-view.getScrollX(), -view.getScrollY());
view.draw(c);
view.setDrawingCacheEnabled(true);
Bitmap cacheBmp = view.getDrawingCache();
Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
view.destroyDrawingCache();
return new BitmapDrawable(viewBmp);
}
I tried to set margin between textview using following code
llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 44);
llp.setMargins(5, 0, 20, 0);
tv.setLayoutParams(llp);
I also set LeftPadding for Textview but seems first textview not getting it.Even i set height to textview but seems textview not getting layout parameter at all. Like
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
44, r.getDisplayMetrics());
tv.setHeight(px);
Please give reference or hint.
Thanks in advance
There are a few problems that I have identified. You need to make the specified changes and everything should work.
Step 1) Update setBounds parameters
In the following line, update the setBounds parameters from -20 to 0 as follows:
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
This is important because you are setting the wrong bounds, which causes tags to overlap.
Step 2) Fix bug in sb.setSpan
If you followed step 1, and you run the code, you will realize that when you attempt to replace text with ImageSpan, you are passing the wrong values (you are not taking into account the ","(comma) character in the end). Update the following line to include -1:
sb.setSpan(new ImageSpan(bd), sb.length() - tags.length() - 1, sb.length() - 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Now you output will appear correct and with comma in the middle.
Step 3) Add spacing between Tags
To answer your original question, how to add spacing, I would recommend that you modify your code to include ", " between different spans. You can also modify it to use just " " space. Define a contentBetweenTags variable and set it to your desired value. Here is how you can do that:
String contentBetweenTags = ", ";
sb.append(tags + contentBetweenTags);
sb1 = new SpannableStringBuilder();
sb1.append(tags + contentBetweenTags);
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length() - contentBetweenTags.length(),
sb.length() - contentBetweenTags.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Step 4) Picking the right "space" character
Just in case you are not happy with the "margin/spacing" between two tags, you could use one of the many unicode space characters available. They have different widths and you could use any one of them based on your desire / liking.
Here is the final code and sample screenshot using unicode \u2002:
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
String contentBetweenTags = ",\u2002";
sb.append(tags + contentBetweenTags);
sb1 = new SpannableStringBuilder();
sb1.append(tags + contentBetweenTags);
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length() - contentBetweenTags.length(),
sb.length() - contentBetweenTags.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
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.