0

Basically I have a paragraph like this

Difference between the lower energy level of conduction band $ec and upper energy level of valence band $ev is called energy band gap.

Now I'm trying to parse through the text, to get those $ prefixed symbols, and replace them with small PNG images that are mathematical formulas. They are just single/dbl letter constant symbols like Ev and Ec. They are in my assets folder. Basically this is the code

public SpannableStringBuilder getConstants(String desc, String constpath){
    SpannableStringBuilder builder = new SpannableStringBuilder();
    if (desc.contains("$")){
        Matcher matcher = Pattern.compile("\\$\\w+").matcher(desc);
        int lastEnding=0;
        while (matcher.find()) {
            String constName = matcher.group();
            constName = constName.substring(1,constName.length());
            int startIndex = matcher.start();
            int endIndex = matcher.end();
            String brokenDescFirstPart = desc.substring(lastEnding, startIndex - 1);
            lastEnding = endIndex+1;
            builder.append(brokenDescFirstPart).append("   ");
            try{
                InputStream imgStream = getContext().getAssets().open(constpath+constName+".png");
                Drawable dconstImg = Drawable.createFromStream(imgStream, null);
                imgStream.close();
                builder.setSpan(new ImageSpan(dconstImg,ImageSpan.ALIGN_BOTTOM), builder.length() -3, builder.length() -1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                builder.append("  ");
            }catch (Exception e){
                builder.append(" ");
            }
        }
        String brokenDescLastPart = desc.substring(lastEnding, desc.length() - 1);
        builder.append(brokenDescLastPart);
    }else{
        builder = SpannableStringBuilder.valueOf(desc);
    }

    return builder;
}

Quick summary of code is that desc is the string I want to parse. I then use regex to get the $word pattern and use the matcher.match method to iterate through the text. I use some int variables to keep track of start and endpoints in between those symbols to carefully reconstruct the original string with images embedded. Now the code

try{
                InputStream imgStream = getContext().getAssets().open(constpath+constName+".png");
                Drawable dconstImg = Drawable.createFromStream(imgStream, null);
                imgStream.close();
                builder.setSpan(new ImageSpan(dconstImg,ImageSpan.ALIGN_BOTTOM), builder.length() -3, builder.length() -1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                builder.append("  ");
            }catch (Exception e){
                builder.append(" ");
            }

is the crux of my question. I got it from some other question on the issue. They said it works. But it doesn't in mine. I've set breakpoints all over the code and gone through line by line execution. No errors nothing whatsoever. Still doesn't work.

The text loads with extra spaces in the textview that I've intentionally used to identify the places where it must have injected the images. My TextView is the Small Text View widget in Android Studio.

azmath
  • 863
  • 1
  • 11
  • 30
  • what does `TextUtils#dumpSpans` show ? also for testing just try `ImageSpan(Context context, int resourceId)` and see what happens – pskink Sep 29 '15 at 12:58
  • I tried using that constructor and it works. But the problem is that I want to load image from assets folder. I realized that I had to call setBounds on the drawable and it now works! (It was rendering the image with default size of 0x0 px) I just have to figure out how to set the width and height so that the images are perfectly inline with the text without sticking out. – azmath Sep 29 '15 at 13:41
  • see `getIntrinsicWidth` / `getIntrinsicHeight` – pskink Sep 29 '15 at 13:49

2 Answers2

2

I was finally able to figure out the issue. Turns out that all drawables MUST be initialized with setBounds() method. Here is my final code

InputStream imgStream = getContext().getAssets().open(constpath + constName + ".png");
                Drawable dconstImg = Drawable.createFromStream(imgStream, null);
                imgStream.close();
                float aspectRatio = (float) (1.00 * dconstImg.getIntrinsicWidth() / dconstImg.getIntrinsicHeight());
                dconstImg.setBounds(0, 0, (int) Math.ceil(descTextSize * aspectRatio), (int) Math.ceil(descTextSize));
                int alignment = s ? ImageSpan.ALIGN_BOTTOM : ImageSpan.ALIGN_BASELINE;
                builder.setSpan(new ImageSpan(dconstImg,alignment), builder.length() -1, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                builder.append(" ");

descTextSize is the result of TextView.getTextSize()

Thanks everyone for your efforts to help me sort this out.

azmath
  • 863
  • 1
  • 11
  • 30
0

Why spannable text when you can use setCompundDrawblesWithIntrinsicBounds? split up the paragraph and attach an unique ID to it. example below;

InputStream imgStream = getContext().getAssets().open(constpath+constName+".png");
Drawable dconstImg = Drawable.createFromStream(imgStream, null);    
if ( Build.VERSION.SDK_INT >= 21 )
{
    text_you_want_to_add_drawable_to.setCompoundDrawablesWithIntrinsicBounds( dconstImg, null ), null, null, null );
} else
{
    text_you_want_to_add_drawable_to.setCompoundDrawablesWithIntrinsicBounds( dconstImg, null, null, null );
}
  • I understand that "text_you_want_to_add_drawable_to" refers to a TextView widget. From the documentation it seems that "setCompundDrawablesWithIntrinsicBounds" is used to set images on the four sides of the textview. But what I want is inline injection of small PNGs. Like using emojis/smilies while typing whatsApp messages. Using Spannable is the only way it seems. – azmath Sep 29 '15 at 13:38