Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to inject BootstrapLabel inside TextView? #189

Open
StNekroman opened this issue Dec 13, 2016 · 9 comments
Open

How to inject BootstrapLabel inside TextView? #189

StNekroman opened this issue Dec 13, 2016 · 9 comments

Comments

@StNekroman
Copy link

Is this possible at all?

Use case:
I need next content to be inserted as text content of text view:

[#hashtag] blah blah blah....
second line, blah blah... <-- second line is inside the same textView (text view is multilined)

@StNekroman
Copy link
Author

I'm looking on https://github.com/binaryfork/Spanny at the moment.
But that would be great to have spannable elements inside this Bootstrap project.

@jamie-beardedhen
Copy link
Contributor

Is it possible to just use the newline character? BootstrapLabel ultimately extends from TextView, and TextView definitely supports this

@StNekroman
Copy link
Author

StNekroman commented Dec 14, 2016

I forgot to add clarification:

[#hashtag] - it's BootstrapLabel.

So whole text
[#hashtag] blah blah blah....\nsecond line, blah blah...
is content of some TextView.

But I cannot insert BootstrapLabel as content of TextView, because BootstrapLabel is not Spannable.
And, as I know, your project doesn't contain bootstrap-like styled Spannable elements.
While I need them.

@StNekroman
Copy link
Author

In Spanny (https://github.com/binaryfork/Spanny) take a look on CustomBackgroundSpan.
That's what I need, but with bootstrap background colors.

@StNekroman
Copy link
Author

StNekroman commented Dec 14, 2016

I ended up with my own impl Span (like CustomBackgroundSpan) which uses BootstrapBrand styling.
I'll post here my solution later.

@jamie-beardedhen
Copy link
Contributor

FYI BootstrapText extends SpannableString, so that might help achieve the same effect?

https://github.com/Bearded-Hen/Android-Bootstrap/blob/master/AndroidBootstrap/src/main/java/com/beardedhen/androidbootstrap/BootstrapText.java

@StNekroman
Copy link
Author

That's what I need:

device-2017-01-03-212645

@StNekroman
Copy link
Author

I found two solutions:

Solution I:

public class BootstrapSpan
        extends CharacterStyle
        implements LineBackgroundSpan, UpdateAppearance {
    private DefaultBootstrapSize bootstrapSize;
    private final BootstrapBrand bootstrapBrand;
    private final Context context;

    private final float baselineCornerRadius;

    private boolean rounded;

    public BootstrapSpan(@NotNull final Context context,
                         @NotNull final BootstrapBrand bootstrapBrand,
                         @NotNull final DefaultBootstrapSize bootstrapSize) {
        super();
        this.context = context;
        this.bootstrapBrand = bootstrapBrand;
        this.bootstrapSize = bootstrapSize;

        baselineCornerRadius = DimenUtils.pixelsFromDpResource(context, com.beardedhen.androidbootstrap.R.dimen.bootstrap_button_default_corner_radius);

        setRounded(false);
    }

    public BootstrapSpan(@NotNull final Context context,
                         @NotNull final BootstrapBrand bootstrapBrand,
                         @NotNull final DefaultBootstrapSize bootstrapSize,
                         final boolean rounded) {
        this(context, bootstrapBrand, bootstrapSize);
        setRounded(rounded);
    }

    public void setRounded(boolean rounded) {
        this.rounded = rounded;
    }

    @Override
    public void drawBackground(Canvas canvas, Paint paint, int left, int right, int top,
                               int baseline, int bottom, CharSequence text, int start, int end, int lnum) {
        float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
        float height = (bottom - top) * bootstrapSize.scaleFactor();
        final int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;
        //final RectF rect = new RectF(left, top, left + width, top + height);
        final RectF rect = new RectF(left, bottom - height, left + width, bottom);
        paint.setColor(bootstrapBrand.defaultFill(context));
        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
    }

    @Override
    public void updateDrawState(final TextPaint paint) {
        paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());
        paint.setColor(bootstrapBrand.defaultTextColor(context));
    }
}

Solution II

public class BootstrapSpan extends ReplacementSpan {
    private DefaultBootstrapSize bootstrapSize;
    private final BootstrapBrand bootstrapBrand;
    private final Context context;

    private final float baselineCornerRadius;

    private boolean rounded;

    public BootstrapSpan(@NotNull final Context context,
                         @NotNull final BootstrapBrand bootstrapBrand,
                         @NotNull final DefaultBootstrapSize bootstrapSize) {
        super();
        this.context = context;
        this.bootstrapBrand = bootstrapBrand;
        this.bootstrapSize = bootstrapSize;

        baselineCornerRadius = DimenUtils.pixelsFromDpResource(context, com.beardedhen.androidbootstrap.R.dimen.bootstrap_button_default_corner_radius);

        setRounded(false);
    }

    public BootstrapSpan(@NotNull final Context context,
                         @NotNull final BootstrapBrand bootstrapBrand,
                         @NotNull final DefaultBootstrapSize bootstrapSize,
                         final boolean rounded) {
        this(context, bootstrapBrand, bootstrapSize);
        setRounded(rounded);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());

        paint.setColor(bootstrapBrand.defaultFill(context));
        float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
        float height = (bottom - top) * bootstrapSize.scaleFactor();
        Log.i("DEBUG_TAG", "Height=" + height);
        int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;

        RectF rect = new RectF(x, top, x + width + cornerRadius, top + height);
        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint);
        paint.setColor(bootstrapBrand.defaultTextColor(context));
        canvas.drawText(text, start, end, x + cornerRadius/2, y * bootstrapSize.scaleFactor(), paint);
    }

    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        paint.setTextSize(paint.getTextSize() * bootstrapSize.scaleFactor());

        float width = paint.measureText(text, start, end) * bootstrapSize.scaleFactor();
        int cornerRadius = rounded ? (int) (baselineCornerRadius * bootstrapSize.scaleFactor()) : 0;
        return Math.round(width + cornerRadius);
    }

    public void setRounded(boolean rounded) {
        this.rounded = rounded;
    }
}

@StNekroman
Copy link
Author

StNekroman commented Jan 3, 2017

Solution 2 is based on ReplacementSpan. It's good in all cases becuase provides full controll over drawing and measing the size of span. But I faced later with strange bug:
If TextView contains two lines of text, and if second line is tagged only by BootstrapSpan, then spannable string (which was tagged by this BootstrapSpan) becomes invisible. Acts like visibility is set to GONE (completely gone, without allocated space)
But if span tag includes only part of string - no bug.
If spanned string is prepeded (or appended to) by anythin else - no bug.
bug happens only if this tag includes whole string in the line.
I believe it's related somehow to ReplacementSpan's internal impl.

Solution 1 is based on LineBackgroundSpan which created to write background.
The only one difference is - in order to write text over the background without introducing another tag I modify text color of TextPaint and it writes the text automatically. It is not very good, because this solution doesn't provide manual control of text rendering. Everything you can do here - modify TextPaint's text color and textSize. But you cannot modify text's measured width.
So you cannot include additional space before and after string (like additional paddingStart/End) - LineBackgroundSpan doesn't provide way to override text measurement.

I choose solution 1 temporarily, because I dont like solutions, where I cannot explain something, even if there is a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants