Align bitmap in SpannableStringBuilder - android

How can I align a bitmap to the text in a SpannableString?
SpannableStringBuilder ssb = new SpannableStringBuilder(arr_messages.get(position));
String msg1 = ssb.toString();
for (int i=0; i<smileys.length; i++)
{
if (msg1.indexOf(smileys[i]) > 0)
{
int t = msg1.indexOf(smileys[i]);
ssb.setSpan(new ImageSpan(bitmapArray.get(i)), t, t+2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
holder.txtName.setText(ssb, BufferType.SPANNABLE);
I would like to demonstrate the problem with these images:
What I want is to center both the text and the image vertically (the text is ok, but the image screws it up).
The result is:

Try ...new ImageSpan(bitmapArray.get(i), ImageSpan.ALIGN_BASELINE)...!

Related

How to create overlapped image span?

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.

EditText is too slow when there are many ImageSpan in it

I want to customize EditText's behavior like HandRite.
So I tried examing EditText's editing performance when there are many ImageSpan, but it was too slow when adding or deleting character on middle using touch screen and IME after below code.
How can I speed up this performance?
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(' ');
}
SpannableString ss = new SpannableString(sb.toString());
Random random = new Random();
for (int i = 0; i < 1000; i++) {
Drawable d = new ColorDrawable(random.nextInt());
d.setBounds(0, 0, mEditText.getLineHeight(), mEditText.getLineHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
ss.setSpan(span, i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mEditText.setText(ss);
HandRite's behavior
Since the number of ImageSpan objects are very large almost 1000 in your SpannableString which is causing it getting slow.
Try to use HTML teext along with ImageGetter and check peformance :
ImageGetter imageGetter = new ImageGetter() {
public Drawable getDrawable(String source) {
StringTokenizer st = new StringTokenizer(index, ".");
Drawable d = new BitmapDrawable(getResources(),emoticons[Integer.parseInt(st.nextToken()) - 1]);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
};
Spanned cs = Html.fromHtml("<img src ='"+ index +"'/>", imageGetter, null);

Text coming from a raw file flowing around an image

this is my first question here.
I'm trying to develop a "book" with text and images and I'm having problems to show a text (loaded from a raw file) flowing around and image in a textview.
I have tried this and it works fine while the text is defined in a string resource. However, when the text comes from an external file (eg a .txt file which includes break lines) the TextView looks something like this:
--------- text text text text text
| | text text text text text
--------- text text text text text
text text text text
text text text text
text text text text
text text text text
That is to say, just after the image, each line leaves an empty space to the right which has the same size than the image.
I don't know why this happens, Am I missing something? this is the code:
ImageView page_im_iv = (ImageView) findViewById(R.id.page_image);
TextView page_text = (TextView) findViewById(R.id.page_text);
Drawable page_image getResources().getDrawable(R.drawable.anyname);
page_im_iv.setBackground(page_image);
float left_margin = page_image.getIntrinsicWidth() + 10;
float top_margin = page_image.getIntrinsicHeight() + 10;
float flines = top_margin/page_text.getTextSize();
int ilines = (int) flines;
StringBuilder raw_text = readRaw(this,res_id);//res_id changes dynamically, it is just the name of the .txt file
SpannableString ss = new SpannableString(raw_text.toString());
ss.setSpan(new MyLeadingMarginSpan2(ilines, left_margin), 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
page_text.setText(ss);
And this is the layout:
<ScrollView xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="example.ActivityBookPage"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/page_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/page_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"/>
</RelativeLayout>
<!-- Other stuff
...
-->
</LinearLayout>
</ScrollView >
Thats all.
In case the problem comes from the readRaw method, this is the code:
public static StringBuilder readRaw(Context ctx,int res_id) {
StringBuilder text = new StringBuilder();
InputStream is = ctx.getResources().openRawResource(res_id);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 8192);
try {
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
isr.close();
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
return text;
}
And this is the code for MyLeadingMarginSpan2 class, copy-pasted from the previous link
public class MyLeadingMarginSpan2 implements 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) {}
}
Finally I found a solution (thanks to this old reply). The problem was that the text coming from the raw file may include break lines (\n), which are not taken into account when calculating "ilines". Hence, we need to count the number of characters that fit just at the right of the image, then include a new break line and then the rest of the text. This is the code
int charCount = page_text_layout.getLineEnd(Math.min(ilines - 1, page_text_layout.getLineCount() - 1));//see below what page_text_layout is
//in case the image is big enough to have all
//the text at its right, just use ss.length as
//the third parameter for setSpan
if (charCount >= ss.length() || charCount <= 0 ) {
ss.setSpan(new MyLeadingMarginSpan2(ilines, left_margin), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
page_text.setText(ss);
}
else { //in case the text is longer, make three blocks
Spannable s1 = new SpannableStringBuilder(ss, 0, charCount);
s1.setSpan(new MyLeadingMarginSpan2(ilines, left_margin), 0, charCount, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spannable s2 = new SpannableStringBuilder(System.getProperty("line.separator"));
Spannable s3 = new SpannableStringBuilder(ss, charCount, ss.length());
page_text.setText(TextUtils.concat(s1, s2, s3));
}
where page_text_layout is defined previously inside an onGlobalLayout callback (in my case inside the onCreate() method):
ViewTreeObserver vto = page_text.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
page_text_layout = page_text.getLayout();
}
});
Of course this code can be refined (eg. checking whether s1 breaks the text in the middle of a word) but this would be the main structure.
Hope this helps someone!

how set margin between spannable textview?

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);

text wrapping around image in android

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.

Categories

Resources