I would like to have a custom Spannable like below picture :
I would like to create a Zigzag line under a incorrect words.
How can I do?
You most definitely want to do some checking on this implementation I hacked together. But still. It's in hopes it still manages to give some basis for implementing such a feature properly.
Actual span class, which, surprisingly ,doesn't replace anything. Only hopes that it really is possible to draw the original span text as-is given two lines of code. Plus additionally draws the 'underline'.
private class ErrorSpan extends ReplacementSpan {
private Paint errorPaint;
public ErrorSpan() {
errorPaint = new Paint();
errorPaint.setColor(Color.RED);
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end,
FontMetricsInt fm) {
return (int)paint.measureText(text, start, end);
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
// Render the red zigzag lines below text
float width = paint.measureText(text, start, end);
canvas.save();
canvas.clipRect(x, bottom - 5, x + width, bottom);
for (float lineX = x; lineX < x + width; lineX += 10) {
canvas.drawLine(lineX, bottom - 5, lineX + 5, bottom, errorPaint);
canvas.drawLine(lineX + 5, bottom, lineX + 10, bottom - 5, errorPaint);
}
canvas.restore();
// Render the span text as-is
canvas.drawText(text, start, end, x, y, paint);
}
};
Pardon me for using magic numbers in line drawing loop (which quite likely could be way more effective too) - but hopefully it manages to give good enough basis for creating production quality implementation at the end.
And usage would be somewhere around:
TextView tv = (TextView)findViewById(R.id.textview);
Spannable spannable = Spannable.Factory.getInstance()
.newSpannable("testtest\ntesttest");
spannable.setSpan(new ErrorSpan(), 4, 8, 0);
spannable.setSpan(new ErrorSpan(), 9, 13, 0);
tv.setText(spannable);
Related
I am trying to merge two textview with two different styles.
In above pic, How to put that curved border textview at end of above text. Means at end of (6).There is an way to do it in html but border not displaying in html.
String html = "<p style=\"border:1px; border-style:solid; border-color:#FF0000; padding: 1em;\"> B1-993m </p>";
holder.book2.setText(Html.fromHtml(
html,
Html.FROM_HTML_MODE_LEGACY));
Above code works if we put it in html file but not working in android.it seems in android some html styles not supported.is there any way to do it or something wrong in my html with android.
Flexlayout :
Not sure about the HTML part i don't have much idea about HTML.
What i understood from your question i think this can be done by SpannableString . All you have to do is create a Custom ReplacementSpan. To apply the Span you need to have the idea about the span indexes i.e where exactly you want to show the box .
I have tried following way . Its just a sample class to get the idea you can modify the drawing as per your need color and style everything.
public class BoxedSpan extends ReplacementSpan {
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRoundRect(rect, 10f, 10f, paint);
paint.setStyle(Paint.Style.FILL);
canvas.drawText(text, start, end, x, y, paint);
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return Math.round(measureText(paint, text, start, end));
}
private float measureText(Paint paint, CharSequence text, int start, int end) {
return paint.measureText(text, start, end);
}
}
here is the sample code i tried :-
SpannableStringBuilder stringBuilder = new
SpannableStringBuilder("Here is some text B1-38 B2-36");
stringBuilder.setSpan(new BoxedSpan(), 18, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
stringBuilder.setSpan(new BoxedSpan(), 24, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(stringBuilder);
Looks like it pretty much does the job . Give it a try.
First of all, you can try to use ConstraintLayout, it will help you arrange all view elements properly, and those two rounded TextView can be done with adding rounded background to them. You don't need any html marking.
You might be better suited to using a background drawable with rounded edges for the B1-38
I have a Calendar inside my Application. The Calendar is a GridView with Buttons for every date. I tried to color them with the following class
public class CircleSpan extends ReplacementSpan {
private final float mPadding;
private final int mCircleColor;
private final int mTextColor;
public CircleSpan(Context context) {
super();
TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{
R.color.current_day,
android.R.attr.textColorPrimaryInverse
});
mCircleColor = ta.getColor(0, ContextCompat.getColor(context, R.color.current_day));
//noinspection ResourceType
mTextColor = ta.getColor(1, 0);
ta.recycle();
mPadding = context.getResources().getDimension(R.dimen.padding_circle);
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
Log.d("CircleSpan", "getSize");
return Math.round(paint.measureText(text, start, end) + mPadding * 2); // left + right
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
if (TextUtils.isEmpty(text)) {
Log.d("CircleSpan", "empty draw");
return;
}
float textSize = paint.measureText(text, start, end);
paint.setColor(mCircleColor);
canvas.drawCircle(x + textSize / 2 + mPadding,
(top + bottom) / 2, // center Y
(text.length() == 1 ? textSize : textSize / 2) + mPadding,
paint);
paint.setColor(mTextColor);
canvas.drawText(text, start, end, mPadding + x, y, paint);
Log.d("CircleSpan", "draw");
}
}
I created the class and tested it with a Lollipop Test Device and everything worked fine. After that I put the Application on my device with Marshmallow. The entries inside the Calendar which should have a color weren't visible anymore. I found out that the draw method inside my CircleSpan class didn't even got called.
With a little "hack" i was able to get it working but I'm really not satisfied by the solution. It consists of a TextView that is not visible on the end of the screen which also gets colored with the CircleSpan. The difference consists of an extension of the text and just color everything except the extension:
// Absolutly hacked
SpannableString spannable1 = new SpannableString(theday + " ");
spannable1.setSpan(new CircleSpan(gridcell.getContext(), ColorType.NONE),
0, theday.length() - 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mHack.setText(spannable1, TextView.BufferType.SPANNABLE);
As long the "hack" is inside the App everything else got colored like I coded it. But I'm really not sure why. I've read about the ReplacementSpan in the Android documentation: ReplacementSpan getSize
But the clue
Returns the width of the span. Extending classes can set the height of the span by updating attributes of Paint.FontMetricsInt. If the span covers the whole text, and the height is not set, draw(Canvas, CharSequence, int, int, float, int, int, int, Paint) will not be called for the span.
doesen't help me. Does anyone has an idea how its possible to color the entries of my calendar with my class and without the "hack"? And why is the problem just on Marshmallow Devices? I'm not sure about Nougat and whats happening on devices below Lollipop.
I hope everyone can understand my poor English. Thanks in advance!
How to create a secondary line(underline) in TextView?
For example
line1:
|word1 word2 word3 word4| //size17
| word2 description | //size 6
line2:
|word4 word5 word6 word7 | //size17
| word7 description | //size6
EDIT 27.05:
preview version. How to improve?
public class DescriptionSpan extends ReplacementSpan {
String description;
Paint descriptionPaint;
public DescriptionSpan(Paint paint, String description) {
descriptionPaint = paint;
this.description = description;
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
float delta = getShift(paint, text.subSequence(start, end).toString(), description);
if (delta >= 0) {
canvas.drawText(text, start, end, x, y, paint);
canvas.drawText(description, 0, description.length(), x + delta, y + descriptionPaint.getTextSize(), descriptionPaint);
} else {
canvas.drawText(text, start, end, x - delta, y, paint);
canvas.drawText(description, 0, description.length(), x, y + descriptionPaint.getTextSize(), descriptionPaint);
}
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return (int) Math.max(paint.measureText(text, start, end), descriptionPaint.measureText(description));
}
private float getShift(Paint paint, String text, String description) {
return (paint.measureText(text) - descriptionPaint.measureText(description)) / 2;
}
I don't like writing answers without code; however, I've spent way too long on this (I can't resist a challenge when it comes to coding) and I think I have enough information to put you in the right direction without a working code sample.
The way it's supposed to work is that in getSize() you would set the FontMetricsInt that are passed in to the proper heights for your span, as well as return the span width.
But here's the thing: The only concrete implementation of ReplacementSpan is ImageSpan which extends DynamicDrawableSpan (which is a ReplacementSpan). If you look at DynamicDrawableSpan, it sets the descent and bottom to zero and the ascent and top to the size of the drawable. More on this in a second.
When I took your code and tried to add the font metrics logic to getSize(), I ran into a couple problems.
Changes in ascent/top are honored, but any change to the bottom -- which would need to the adjusted to allow room for your description -- is not honored. It seems that the logic is tailored to accommodate ImageSpan/DynamicDrawableSpan, and everything else is treated as an untested corner case.
ReplacementSpan seems to be targeted for single-line texts. The ascent & top are correctly set up in the first paragraph line, but after the line wraps, the normal ascent/top are restored.
The span is generally treated as a non-breaking word, but I did run into an intermittent bug where the span itself was wrapped onto a second line. Obviously this would be a problem with calculating where to put all your texts.
So if you really wanted to do this with spans, I think what you would have to do is have another custom paragraph-style span that implements LineHeightSpan. For this you would implement chooseHeight(). How this works is that chooseHeight() would be called for each line in the paragraph. Your implementation would look for a DescriptionSpan on that line and set the font metrics to accommodate the description text. So you would wrap each paragraph that had DescriptionSpans with this paragraph-style span. With this span, your DescriptionSpan wouldn't alter the font metrics in getSize(), and you'd just assume you have space for the description text in draw(). (I hope the canvas isn't clipped to the span bounds. That custom view idea just keeps looking better and better.)
I would love to get the code working and post it, but I spend too much time banging my head on ReplacementSpan font metrics.
You can use a SpannableStringBuilder for this. Something like below :
SpannableStringBuilder span = new SpannableStringBuilder();
span.append("First Line");
span.setSpan(new RelativeSizeSpan(.8f),0,firstLineLength);
span.append("Second Line");
span.setSpan(new RelativeSizeSpan(1f),firstLineLength,firstLineLength+secondLineLength);
textView.setText(span);
My goal is to create this badge-like view used in Gmail and Foursquare as given below.
So far I have created ReplacementSpan to handle foreground and background color of individual view.
public class SearchTagSpan extends ReplacementSpan {
private int backgroundColor;
private int forgroundColor;
public SearchTagSpan() {
backgroundColor = -1;
forgroundColor = Color.BLACK;
}
public SearchTagSpan(int backgroundColor, int forgroundColor) {
this.backgroundColor = backgroundColor;
this.forgroundColor = forgroundColor;
}
#Override
public void draw(
Canvas canvas,
CharSequence text, int start, int end,
float x, int top, int y, int bottom,
Paint paint) {
RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
paint.setColor(backgroundColor);
canvas.drawRect(rect, paint);
paint.setColor(forgroundColor);
canvas.drawText(text, start, end, x, y, paint);
}
#Override
public int getSize(
Paint paint,
CharSequence text, int start, int end,
Paint.FontMetricsInt fm) {
return Math.round(paint.measureText(text, start, end));
}
private float measureText(Paint paint, CharSequence text, int start, int end) {
return paint.measureText(text, start, end);
}
}
Both background and foreground color is applied correctly however when the view becomes multiple-line the background of each badge stretch to fill the line spacing specified in the TextView layout. If I remove line spacing then the background of the first and second line are touching each other as shown in the image below.
Am I going the right direction here? Is there something I missed?
Take a look at these
https://plus.google.com/+RomanNurik/posts/WUd7GrfZfiZ
and
https://github.com/splitwise/TokenAutoComplete
and
https://github.com/kpbird/chips-edittext-library
For that you need ActionBar.
This supports above +3.0
If you need same in lower version of android then you can use appCompat support library in order to support lower version of android.
Kindly refer .. http://developer.android.com/guide/topics/ui/actionbar.html
and best working example tutorial over here ... http://www.androidhive.info/2013/11/android-working-with-action-bar/
Hope it helps you.
Currently I have a SpannableString object with multiple Clickable objects set to it. So one string has many Clickable objects and depending on which word/section the User clicks the app will go on and do the processing of that click event. The other day I had asked here on stackoverflow about getting rid of the blue underline on part of the word in the SpannableString and the answer was to sub class the ClickableSpan class, and override the updateDrawState method and setting the underlineText to false which worked.
My Problem:
Is it possible to put a border around the Clickable object in the SpannableString? So basically each Clickable object/string has to have there own border.
I thought maybe the updateDrawState method maybe able to help but it didn't. Does anybody know how this can be achieved?
Thanks.
I extended ReplacementSpan to make an outlined span. Unfortunately, I can't manage to make them wrap, but if you're only looking to apply your outline to a couple words, it should work fine. To make this clickable, you'd just use the subclass you mentioned setSpan(ClickableSpanWithoutUnderline...) before you set this one.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_replacement_span);
final Context context = this;
final TextView tv = (TextView) findViewById(R.id.tv);
Spannable span = Spannable.Factory.getInstance().newSpannable("Some string");
span.setSpan(new BorderedSpan(context), 0, span.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(span, TextView.BufferType.SPANNABLE);
}
public static class BorderedSpan extends ReplacementSpan {
final Paint mPaintBorder, mPaintBackground;
int mWidth;
Resources r;
int mTextColor;
public BorderedSpan(Context context) {
mPaintBorder = new Paint();
mPaintBorder.setStyle(Paint.Style.STROKE);
mPaintBorder.setAntiAlias(true);
mPaintBackground = new Paint();
mPaintBackground.setStyle(Paint.Style.FILL);
mPaintBackground.setAntiAlias(true);
r = context.getResources();
mPaintBorder.setColor(Color.RED);
mPaintBackground.setColor(Color.GREEN);
mTextColor = Color.BLACK;
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
//return text with relative to the Paint
mWidth = (int) paint.measureText(text, start, end);
return mWidth;
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
canvas.drawRect(x, top, x + mWidth, bottom, mPaintBackground);
canvas.drawRect(x, top, x + mWidth, bottom, mPaintBorder);
paint.setColor(mTextColor); //use the default text paint to preserve font size/style
canvas.drawText(text, start, end, x, y, paint);
}
}