Android custom horizontal progressbar with progress dividers - android

I need to make a custom horizontal progressbar in Android, but it doesn't have to be continuous. It must have DIVIDERS for every progress value. For example: it must have green progress and white dividers. Please see the next image to understand what I'm looking for. Thank you.
For now I am trying to make a custom Drawable and set it to the progress bar.
public class CustomProgressDrawable extends Drawable {
private static final int DEFAULT_RECT_WIDHT = 20;
private static int DEFAULT_RECT_COLOR;
private int rectWidth;
private int rectColor;
Paint paint = new Paint();
public CustomProgressDrawable(Context context){
init(context);
}
private void init(Context context){
DEFAULT_RECT_COLOR = context.getResources().getColor(android.R.color.black);
rectColor = DEFAULT_RECT_COLOR;
rectWidth = DEFAULT_RECT_WIDHT; // Default rect width
}
#Override
public void draw(Canvas canvas) {
float parentWidth = 300;
float parentHeight = 40;
float childTop = 0;
float childBottom = parentHeight;
for (int i = rectWidth; i < parentWidth; i =+ rectWidth){
paint.setColor(rectColor);
canvas.drawRect(i - rectWidth + 10, childTop, i + 10, childBottom, paint);
}
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return super.onLevelChange(level);
}
}
And in my may activity I have:
CustomProgressDrawable customProgressDrawable = new CustomProgressDrawable(this);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgressDrawable(customProgressDrawable);
progressBar.setProgress(50);

Related

Indeterminate ProgressBar with round edges

I am simply trying to customize the default indeterminate progressbar. Please how do I go from this
<ProgressBar
android:layout_width="100dp"
android:layout_height="100dp"
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="#FFFFFF"/>
asper:
to this (easily):
Update: seems the question isn't a bit straight forward so I have added .gifs below to further explain, I want this (with sharp edges): to simply become this (with round edges):
sorry about the color change, that was the best google turned up
You can use https://github.com/korre/android-circular-progress-bar
There you have a method useRoundedCorners you need to pass false to make it not round by-default it is round at the edge
There is a custom class you can actually take from that library(It is more than enough),
public class CircularProgressBar extends View {
private int mViewWidth;
private int mViewHeight;
private final float mStartAngle = -90; // Always start from top (default is: "3 o'clock on a watch.")
private float mSweepAngle = 0; // How long to sweep from mStartAngle
private float mMaxSweepAngle = 360; // Max degrees to sweep = full circle
private int mStrokeWidth = 20; // Width of outline
private int mAnimationDuration = 400; // Animation duration for progress change
private int mMaxProgress = 100; // Max progress to use
private boolean mDrawText = true; // Set to true if progress text should be drawn
private boolean mRoundedCorners = true; // Set to true if rounded corners should be applied to outline ends
private int mProgressColor = Color.BLACK; // Outline color
private int mTextColor = Color.BLACK; // Progress text color
private final Paint mPaint; // Allocate paint outside onDraw to avoid unnecessary object creation
public CircularProgressBar(Context context) {
this(context, null);
}
public CircularProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
initMeasurments();
drawOutlineArc(canvas);
if (mDrawText) {
drawText(canvas);
}
}
private void initMeasurments() {
mViewWidth = getWidth();
mViewHeight = getHeight();
}
private void drawOutlineArc(Canvas canvas) {
final int diameter = Math.min(mViewWidth, mViewHeight) - (mStrokeWidth * 2);
final RectF outerOval = new RectF(mStrokeWidth, mStrokeWidth, diameter, diameter);
mPaint.setColor(mProgressColor);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(mRoundedCorners ? Paint.Cap.ROUND : Paint.Cap.BUTT);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(outerOval, mStartAngle, mSweepAngle, false, mPaint);
}
private void drawText(Canvas canvas) {
mPaint.setTextSize(Math.min(mViewWidth, mViewHeight) / 5f);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(0);
mPaint.setColor(mTextColor);
// Center text
int xPos = (canvas.getWidth() / 2);
int yPos = (int) ((canvas.getHeight() / 2) - ((mPaint.descent() + mPaint.ascent()) / 2)) ;
canvas.drawText(calcProgressFromSweepAngle(mSweepAngle) + "%", xPos, yPos, mPaint);
}
private float calcSweepAngleFromProgress(int progress) {
return (mMaxSweepAngle / mMaxProgress) * progress;
}
private int calcProgressFromSweepAngle(float sweepAngle) {
return (int) ((sweepAngle * mMaxProgress) / mMaxSweepAngle);
}
/**
* Set progress of the circular progress bar.
* #param progress progress between 0 and 100.
*/
public void setProgress(int progress) {
ValueAnimator animator = ValueAnimator.ofFloat(mSweepAngle, calcSweepAngleFromProgress(progress));
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(mAnimationDuration);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mSweepAngle = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.start();
}
public void setProgressColor(int color) {
mProgressColor = color;
invalidate();
}
public void setProgressWidth(int width) {
mStrokeWidth = width;
invalidate();
}
public void setTextColor(int color) {
mTextColor = color;
invalidate();
}
public void showProgressText(boolean show) {
mDrawText = show;
invalidate();
}
/**
* Toggle this if you don't want rounded corners on progress bar.
* Default is true.
* #param roundedCorners true if you want rounded corners of false otherwise.
*/
public void useRoundedCorners(boolean roundedCorners) {
mRoundedCorners = roundedCorners;
invalidate();
}}
Then you can set the view in your xml like
<yourPackageName.CircularProgressBar
android:id="#+id/circularProgress"
android:layout_width="180dp"
android:layout_height="180dp"/>
And in your class you can call it like this,
CircularProgressBar circularProgressBar = (CircularProgressBar)
findViewById(R.id.circularProgress);
circularProgressBar.setProgress(50);
circularProgressBar.setProgressColor(Color.BLUE);

How to apply horizontal fading edge on the last line of a maxLine TextView

I'd like to implement a fading edge behavior to TextView like the Google Play Movie does:
As you can see the last letters of the third line have a fading edge effect.
Is there a way to achieve this for a specific line defined via android:maxLines? (For example android:maxLines="3")
I've tried the following but it works with the attribute android:singleLine only which is not my goal:
<TextView
...
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="30dp"
android:ellipsize="none"
android:singleLine="true" />
Setting android:maxLines here instead results in no fading at all.
Edit/Additional:
Previously I also tried a Shader with LinearGradient while extending TextView like here, but the described solution applies a background/foreground (and there were also some other issues with it ...).
I'd like to apply the Gradient to the last 3-4 characters of the maxLine line. Could this be possible?
Edit:
With Mike M.'s help (take a look into the comments) I could modify his answer to reach my wanted behavior. The final implementation with additions (or here as java file):
public class FadingTextView extends AppCompatTextView {
// Length
private static final float PERCENTAGE = .9f;
private static final int CHARACTERS = 6;
// Attribute for ObjectAnimator
private static final String MAX_HEIGHT_ATTR = "maxHeight";
private final Shader shader;
private final Matrix matrix;
private final Paint paint;
private final Rect bounds;
private int mMaxLines;
private boolean mExpanded = false;
public FadingTextView(Context context) {
this(context, null);
}
public FadingTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public FadingTextView(Context context, AttributeSet attrs, int defStyleAttribute) {
super(context, attrs, defStyleAttribute);
matrix = new Matrix();
paint = new Paint();
bounds = new Rect();
shader = new LinearGradient(0f, 0f, PERCENTAGE, 0f, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mMaxLines = getMaxLines();
}
#Override
protected void onDraw(Canvas canvas) {
if (getLineCount() > getMaxLines() && !mExpanded
&& getRootView() != null && getText() != null
) {
final Matrix m = matrix;
final Rect b = bounds;
final Layout l = getLayout();
int fadeLength = (int) (getPaint().measureText(getText(), getText().length() - CHARACTERS, getText().length()));
final int line = mMaxLines - 1;
getLineBounds(line, b);
final int lineStart = l.getLineStart(line);
final int lineEnd = l.getLineEnd(line);
final CharSequence text = getText().subSequence(lineStart, lineEnd);
final int measure = (int) (getPaint().measureText(text, 0, text.length()));
b.right = b.left + measure;
b.left = b.right - fadeLength;
final int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
super.onDraw(canvas);
m.reset();
m.setScale(fadeLength, 1f);
m.postTranslate(b.left, 0f);
shader.setLocalMatrix(matrix);
canvas.drawRect(b, paint);
canvas.restoreToCount(saveCount);
} else {
super.onDraw(canvas);
}
}
/**
* Makes the TextView expanding without any animation.
*/
public void expandCollapse() {
setMaxLines(mExpanded ? mMaxLines : getLineCount());
mExpanded = !mExpanded;
}
/**
* Makes the TextView expanding/collapsing with sliding animation (vertically)
*
* #param duration Duration in milliseconds from beginning to end of the animation
*/
public void expandCollapseAnimated(final int duration) {
// Height before the animation (either maxLine or lineCount, depending on current state)
final int startHeight = getMeasuredHeight();
// Set new maxLine value depending on current state
setMaxLines(mExpanded ? mMaxLines : getLineCount());
mExpanded = !mExpanded;
// Measuring new height
measure(View.MeasureSpec.makeMeasureSpec(
getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
);
final int endHeight = getMeasuredHeight();
ObjectAnimator animation = ObjectAnimator.ofInt(
this, // TextView
MAX_HEIGHT_ATTR, // maxHeight
startHeight, // height before animation
endHeight // height after animation
);
animation.setDuration(duration).start();
}
/**
* Sets maxLine value programmatically
*
* #param newValue new value for maxLines
*/
public void setNewMaxLine(int newValue) {
mMaxLines = newValue;
}
}
Try this out .. Hope this will work
public class ShadowSpan : Android.Text.Style.CharacterStyle
{
public float Dx;
public float Dy;
public float Radius;
public Android.Graphics.Color Color;
public ShadowSpan(float radius, float dx, float dy, Android.Graphics.Color color)
{
Radius = radius; Dx = dx; Dy = dy; Color = color;
}
public override void UpdateDrawState (TextPaint tp)
{
tp.SetShadowLayer(Radius, Dx, Dy, Color);
}
}

Can't change vector drawable for a custom view during runtime

I have implemented a custom view which looks like a seekbar with drawable attached at end Custom Score bar. I have provided methods to change colour for progress, change width of the progress(along with the rocket icon), and change the icon itself(for different colours).
public class RocketView extends View {
private int mProgressWidth = 12;
private Drawable mIndicatorIcon;
private int mProgress, mMin = 0, mMax = 100;
private Paint mBackgroundPaint, mProgressPaint;
private RectF mBackgroundRect = new RectF();
private RectF mProgressRect = new RectF();
private int mIndicatorSize;
private int mProgressColor;
private int mIndicatorLeft;
public RocketView(Context context) {
super(context);
init(context, null);
}
public RocketView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
void init(Context context, AttributeSet attrs) {
float density = getResources().getDisplayMetrics().density;
int backgroundColor = ContextCompat.getColor(context, R.color.color_bg);
int progressColor = ContextCompat.getColor(context, R.color.color_progress);
mProgressWidth = (int) (mProgressWidth * density);
mIndicatorIcon = ContextCompat.getDrawable(context, R.drawable.rocket_15);
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RocketView, 0, 0);
Drawable indicatorIcon = a.getDrawable(R.styleable.RocketView_indicatorIcon);
if (indicatorIcon != null) mIndicatorIcon = indicatorIcon;
mProgress = a.getInteger(R.styleable.RocketView_progress, 0);
mProgressWidth = (int) a.getDimension(R.styleable.RocketView_thickness, mProgressWidth);
mIndicatorSize = 10*mProgressWidth;
mIndicatorIcon.setBounds(0, 0, mIndicatorSize, mIndicatorSize);
mProgressColor = a.getColor(R.styleable.RocketView_progressColor, progressColor);
backgroundColor = a.getColor(R.styleable.RocketView_backgroundColor, backgroundColor);
a.recycle();
}
// range check
mProgress = (mProgress > mMax) ? mMax : mProgress;
mProgress = (mProgress < mMin) ? mMin : mProgress;
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(backgroundColor);
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint.setStrokeWidth(mProgressWidth);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint = new Paint();
mProgressPaint.setColor(mProgressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mProgressPaint.setStrokeWidth(mProgressWidth);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int top = 0;
int left = 0;
//normalizing the value from 0 to 100 to 0 to container width
/* Formula for linear range [A..B] to [C..D],
*
* (D-C)*(X-A)
X' = ----------- + C
(B-A)
*/
int extraOffset = dpToPx(2);
int extraPadding = dpToPx(8);
int x = (width - left)*(mProgress - 0)/(100 -0) + 0;
top = 2*mIndicatorSize/5 + mProgressWidth/2 +extraOffset;
mBackgroundRect.set(left +extraPadding, top, left + width -extraPadding, top+mProgressWidth/2 );
int indicatorLeft = width - (left + x);
if (indicatorLeft < mIndicatorSize) {
mIndicatorLeft = width - mIndicatorSize;
mProgressRect.set(left +extraPadding, top, left + x - dpToPx(20), top + mProgressWidth/2 );
}
else {
mIndicatorLeft = left + x - dpToPx(16);
mProgressRect.set(left +extraPadding, top, left + x, top + mProgressWidth/2);
}
setMeasuredDimension(width, mIndicatorSize);
}
#Override
protected void onDraw(Canvas canvas) {
// draw the background
canvas.drawRoundRect(mBackgroundRect, mProgressWidth/2, mProgressWidth/2, mBackgroundPaint);
//draw progress
canvas.drawRoundRect(mProgressRect, mProgressWidth/2, mProgressWidth/2, mProgressPaint);
// draw the indicator icon
canvas.translate(mIndicatorLeft,0);
mIndicatorIcon.draw(canvas);
}
public static int dpToPx(int dp){
return dp * (Resources.getSystem().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
public void setIndicatorIcon(int indicatorIcon) {
mIndicatorIcon = getResources().getDrawable(indicatorIcon);
invalidate();
}
public void setProgress(int progress) {
mProgress = progress;
invalidate();
}
public void setProgressColor(int color) {
mProgressColor = color;
mProgressPaint = new Paint();
mProgressPaint.setColor(mProgressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mProgressPaint.setStrokeWidth(mProgressWidth);
invalidate();
}
}
now i am instantiating it in activity as:
RocketView rv = (RocketView) findViewById(R.id.rv);
rv.setProgressColor(getResources().getColor(R.color.colorAccent));
rv.setProgress(100);
rv.setIndicatorIcon(R.drawable.rocket_15);
If I don't set indicator icon in activity the icon is shown but as soon as I call the method setIndicatorIcon() the icon disappears. What am I missing?

Custom VIew Onclick Style change

I have custom animated play/pause button which is getting highlighted when I press it. How can I remove the highlighted gray color around the button.
Setting background to transparent isn't working.
public class PlayPauseMiniView extends FrameLayout {
private static final long PLAY_PAUSE_ANIMATION_DURATION = 200;
private final PlayPauseDrawable mDrawable;
private final Paint mPaint = new Paint();
private AnimatorSet mAnimatorSet;
private int mWidth;
private int mHeight;
public PlayPauseMiniView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mDrawable = new PlayPauseDrawable(context);
mDrawable.setCallback(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(120, 120);
}
#Override
protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mDrawable.setBounds(0, 0, w, h);
mWidth = w;
mHeight = h;
}
#Override
protected boolean verifyDrawable(Drawable who) {
return who == mDrawable || super.verifyDrawable(who);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mDrawable.draw(canvas);
}
public void toggle() {
if (mAnimatorSet != null) {
mAnimatorSet.cancel();
}
mAnimatorSet = new AnimatorSet();
final Animator pausePlayAnim = mDrawable.getPausePlayAnimator();
mAnimatorSet.setInterpolator(new DecelerateInterpolator());
mAnimatorSet.setDuration(PLAY_PAUSE_ANIMATION_DURATION);
mAnimatorSet.playTogether(pausePlayAnim);
mAnimatorSet.start();
}
}
public class PlayPauseDrawable extends Drawable {
private static final Property<PlayPauseDrawable, Float> PROGRESS =
new Property<PlayPauseDrawable, Float>(Float.class, "progress") {
#Override
public Float get(PlayPauseDrawable d) {
return d.getProgress();
}
#Override
public void set(PlayPauseDrawable d, Float value) {
d.setProgress(value);
}
};
private final Path mLeftPauseBar = new Path();
private final Path mRightPauseBar = new Path();
private final Paint mPaint = new Paint();
private final RectF mBounds = new RectF(0,0,10,10);
private final float mPauseBarWidth;
private final float mPauseBarHeight;
private final float mPauseBarDistance;
private float mWidth;
private float mHeight;
private float mProgress;
private boolean mIsPlay;
public PlayPauseDrawable(Context context) {
final Resources res = context.getResources();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.DKGRAY);
mPauseBarWidth = res.getDimensionPixelSize(R.dimen.pause_bar_width);
mPauseBarHeight = res.getDimensionPixelSize(R.dimen.pause_bar_height);
mPauseBarDistance = res.getDimensionPixelSize(R.dimen.pause_bar_distance);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mBounds.set(bounds);
mWidth = mBounds.width();
mHeight = mBounds.height();
}
#Override
public void draw(Canvas canvas) {
mLeftPauseBar.rewind();
mRightPauseBar.rewind();
// The current distance between the two pause bars.
final float barDist = lerp(mPauseBarDistance, 0, mProgress);
// The current width of each pause bar.
final float barWidth = lerp(mPauseBarWidth, mPauseBarHeight / 2f, mProgress);
// The current position of the left pause bar's top left coordinate.
final float firstBarTopLeft = lerp(0, barWidth, mProgress);
// The current position of the right pause bar's top right coordinate.
final float secondBarTopRight = lerp(2 * barWidth + barDist, barWidth + barDist, mProgress);
// Draw the left pause bar. The left pause bar transforms into the
// top half of the play button triangle by animating the position of the
// rectangle's top left coordinate and expanding its bottom width.
mLeftPauseBar.moveTo(0, 0);
mLeftPauseBar.lineTo(firstBarTopLeft, -mPauseBarHeight);
mLeftPauseBar.lineTo(barWidth, -mPauseBarHeight);
mLeftPauseBar.lineTo(barWidth, 0);
mLeftPauseBar.close();
// Draw the right pause bar. The right pause bar transforms into the
// bottom half of the play button triangle by animating the position of the
// rectangle's top right coordinate and expanding its bottom width.
mRightPauseBar.moveTo(barWidth + barDist, 0);
mRightPauseBar.lineTo(barWidth + barDist, -mPauseBarHeight);
mRightPauseBar.lineTo(secondBarTopRight, -mPauseBarHeight);
mRightPauseBar.lineTo(2 * barWidth + barDist, 0);
mRightPauseBar.close();
canvas.save();
// Translate the play button a tiny bit to the right so it looks more centered.
canvas.translate(lerp(0, mPauseBarHeight / 8f, mProgress), 0);
// (1) Pause --> Play: rotate 0 to 90 degrees clockwise.
// (2) Play --> Pause: rotate 90 to 180 degrees clockwise.
final float rotationProgress = mIsPlay ? 1 - mProgress : mProgress;
final float startingRotation = mIsPlay ? 90 : 0;
canvas.rotate(lerp(startingRotation, startingRotation + 90, rotationProgress), mWidth / 2f, mHeight / 2f);
// Position the pause/play button in the center of the drawable's bounds.
canvas.translate(mWidth / 2f - ((2 * barWidth + barDist) / 2f), mHeight / 2f + (mPauseBarHeight / 2f));
// Draw the two bars that form the animated pause/play button.
canvas.drawPath(mLeftPauseBar, mPaint);
canvas.drawPath(mRightPauseBar, mPaint);
canvas.restore();
}
public Animator getPausePlayAnimator() {
final Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, mIsPlay ? 1 : 0, mIsPlay ? 0 : 1);
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mIsPlay = !mIsPlay;
}
});
return anim;
}
public boolean isPlay() {
return mIsPlay;
}
private void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
private float getProgress() {
return mProgress;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
invalidateSelf();
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
/**
* Linear interpolate between a and b with parameter t.
*/
private static float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
}
You can use ImageView instead of ImageButton
Have You tried android:background="#null" ?

Android : How to create a custom textview?

Im trying to implement a textview with each character bounded in a box with a background color.
A sample is such as below
The text will be totally dynamic.
Im thinking of adding a list of TextViews to a LinearLayout in activity class, and customize their background colors and borders.
Is this is the better approach ?
Please suggest a good solution, if there's any.
here is a simple example, to do this I merely write a view extends TextView.
public class CustomText extends View {
private int borderWidthLeft = dp(4);
private int borderWidthRight = dp(4);
private int borderWidthTop = dp(4);
private int borderWidthBottom = dp(4);
private int boderColor = Color.BLACK;
private int backgroundColor = Color.BLUE;
private int textColor = Color.WHITE;
private int textSize = sp(30);
private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
private Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
private int backgroundRectWidth = dp(35);
private int backgroundRectHeight = dp(35);
private Rect textBgRect = new Rect();
private String defaultText = "A";
public CustomText(Context context) {
this(context, null);
}
public CustomText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
backgroundPaint.setColor(backgroundColor);
textPaint.setColor(textColor);
textPaint.setTextAlign(Align.CENTER);
textPaint.setTextSize(textSize);
textPaint.setFakeBoldText(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);
drawText(canvas);
}
private void drawBackground(Canvas canvas) {
canvas.drawColor(boderColor);
int left = borderWidthLeft;
int top = borderWidthTop;
int right = borderWidthLeft + backgroundRectWidth;
int bottom = borderWidthTop + backgroundRectHeight;
textBgRect.set(left, top, right, bottom);
canvas.save();
canvas.clipRect(textBgRect, Op.REPLACE);
canvas.drawRect(textBgRect, backgroundPaint);
canvas.restore();
}
private void drawText(Canvas canvas) {
int bgCenterX = borderWidthLeft + backgroundRectWidth / 2;
int bgCenterY = borderWidthTop + backgroundRectHeight / 2;
FontMetrics metric = textPaint.getFontMetrics();
int textHeight = (int) Math.ceil(metric.descent - metric.ascent);
int x = bgCenterX;
int y = (int) (bgCenterY + textHeight / 2 - metric.descent);
System.out.println(textHeight);
System.out.println(y);
System.out.println(bgCenterY);
canvas.drawText(defaultText, x, y, textPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(backgroundRectWidth + borderWidthLeft + borderWidthRight,
backgroundRectHeight + borderWidthTop + borderWidthBottom);
}
private int dp(int value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}
private int sp(int value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, getResources().getDisplayMetrics());
}
}
Using separate TextViews is an expensive approach. If your text has many characters, it will cost a lot of memory and time due to the numerous TextView instances' layout and measure passes. Since this is a very specific component, I would recommend creating a custom View and drawing the contents on the canvas manually.
You could use Spannable
Here is a good tutorial https://blog.stylingandroid.com/introduction-to-spans/

Categories

Resources