How to align drawableLeft to top instead center in android TextView? - android

In TextView I set drawableLeft where the drawable is showing from center. I need to align the drawableLeft with top inside TextView like this image.
Is it possible to achieve this?

Use SpannableString and ImageSpan to achieve this.
String msg=" "+"haii";
ImageSpan mImageSpan== new ImageSpan(mContext, R.drawable.icon);
SpannableString text = new SpannableString(msg);
text.setSpan(mImageSpan, 0, 1, 0);
mTextView.setText(text);
the extra space in the string variable is replaced by the icon.

I think it's even easier than all the answers above: You only need to do this:
public class TopGravityDrawable extends BitmapDrawable {
public TopGravityDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
#Override
public void draw(Canvas canvas) {
int halfCanvas = canvas.getHeight() / 2;
int halfDrawable = getIntrinsicHeight() / 2;
canvas.save();
canvas.translate(0, -halfCanvas + halfDrawable);
super.draw(canvas);
canvas.restore();
}
}
And then
final Bitmap bitmap = BitmapFactory.decodeResource(mTitle.getResources(), R.drawable.icon);
icon = new TopGravityDrawable(mTitle.getResources(), bitmap);
title.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
Remember this only works properly with LEFT and RIGHT compound drawables

See my answer here.
You can align a compound-Drawable to the top (or bottom) by creating a custom Drawable that wraps your Drawable, and then manipulate the drawing of your custom Drawable by overriding the method onDraw(Canvas).
The sample below is the simplest possible example. This aligns the image to the top, but you can also make it align to the bottom, left or right of the TextView by implementing the required logic in the onDraw(Canvas)-method. You might also want to build in a margin in the onDraw(Canvas), to make your design implementation pixel perfect.
Sample usage:
GravityCompoundDrawable gravityDrawable = new GravityCompoundDrawable(innerDrawable);
// NOTE: next 2 lines are important!
innerDrawable.setBounds(0, 0, innerDrawable.getIntrinsicWidth(), innerDrawable.getIntrinsicHeight());
gravityDrawable.setBounds(0, 0, innerDrawable.getIntrinsicWidth(), innerDrawable.getIntrinsicHeight());
mTextView.setCompoundDrawables(gravityDrawable, null, null, null);
Sample code:
public class GravityCompoundDrawable extends Drawable {
// inner Drawable
private final Drawable mDrawable;
public GravityCompoundDrawable(Drawable drawable) {
mDrawable = drawable;
}
#Override
public int getIntrinsicWidth() {
return mDrawable.getIntrinsicWidth();
}
#Override
public int getIntrinsicHeight() {
return mDrawable.getIntrinsicHeight();
}
#Override
public void draw(Canvas canvas) {
int halfCanvas= canvas.getHeight() / 2;
int halfDrawable = mDrawable.getIntrinsicHeight() / 2;
// align to top
canvas.save();
canvas.translate(0, -halfCanvas + halfDrawable);
mDrawable.draw(canvas);
canvas.restore();
}
}

you can do like this:
public class DrawableTopLeftTextView extends TextView {
public DrawableTopLeftTextView(Context context) {
super(context);
}
public DrawableTopLeftTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawableTopLeftTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
if (!TextUtils.isEmpty(getText())) {
Drawable[] drawables = getCompoundDrawables();
if (drawables != null) {
Drawable drawableLeft = drawables[0];
if (drawableLeft != null) {
Paint.FontMetricsInt fontMetricsInt = getPaint().getFontMetricsInt();
Rect bounds = new Rect();
getPaint().getTextBounds((String) getText(), 0, length(), bounds);
int textVerticalSpace = Math.round(bounds.top - fontMetricsInt.top);
int offset = (getHeight() - drawableLeft.getIntrinsicHeight()) / 2 - textVerticalSpace - getPaddingTop() / 2;
drawableLeft.setBounds(0, -offset, drawableLeft.getIntrinsicWidth(), drawableLeft.getIntrinsicHeight() - offset);
}
}
}
super.onDraw(canvas);
}
}

If you want a purely XML solution then you can use an inset drawable to re-position your desired drawable.
<?xml version="1.0" encoding="utf-8"?>
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/actual_image_to_be_shown"
android:insetTop="-32dp" />
You might have to play around with the inset value depending on your scenario. Then just use this XML drawable in your TextView drawableLeft/Start definition.

Your can use by this way. May it can helpful you.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/imageView1"
android: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 Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text" />
</RelativeLayout>

Try like this:
class MyTextView #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, style) {
private val leftDrawable = ContextCompat.getDrawable(context, R.drawable.checkmark)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
setBulletPoint(compoundDrawables[0], canvas)
}
private fun setBulletPoint(drawableLeft: Drawable?, canvas: Canvas?) {
if (!TextUtils.isEmpty(text)) {
leftDrawable?.let { drlft ->
if (lineCount == 1) {
setCompoundDrawablesWithIntrinsicBounds(drlft, null, null, null)
} else {
val buttonWidth = drlft.intrinsicWidth
val buttonHeight = drlft.intrinsicHeight
val topSpace = abs(buttonHeight - lineHeight) / 2
drlft.setBounds(0, topSpace, buttonWidth, topSpace + buttonHeight)
canvas?.apply {
save()
drlft.draw(canvas)
restore()
}
}
}
}
}
}

Related

What's the easiest way to add arrow for Android Button

I need to add Arrow to Next Button like that
What's the easiest way to achieve that? Making custom Compound view sounds like overkill
PS Symbol > is not nice.
Just use a TextView.
<TextView
android:id="#+id/some_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/next"
android:drawableRight="#drawable/arrow_right"
android:clickable="true"
android:focusable="true"
android:background="?android:selectableItemBackground"
/>
TextViews have support for compound drawables, where you can specify a drawable you want to be displayed next to the text. It can be above, below, right, left, start, end.
Notice also that I set the TextView to be clickable and focusable and that I gave it Android's default ripple background (where it shows the ripple effect when pressed).
EDIT
If you need the arrow to be directly after the text, you have to use a container and child Views:
<LinearLayout
android:id="#+id/button_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="?android:selectableItemBackground">
<TextView
android:id="#+id/some_textview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="#string/next" />
<ImageView
android:id="#+id/some_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/arrow_right" />
</LinearLayout>
You'll want to set your click listener on the LinearLayout.
Alternatively, use the U+276fa character as part of your TextView's text: ❯. It's not > and looks more like an arrow.
Try this custom button, it calculates where to put the icon directly next to the text.
public class CenteredIconButton extends Button {
private static final int LEFT = 0, TOP = 1, RIGHT = 2, BOTTOM = 3;
private Rect textBounds = new Rect();
private Rect drawableBounds = new Rect();
public CenteredIconButton(Context context) {
this(context, null);
}
public CenteredIconButton(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.buttonStyle);
}
public CenteredIconButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) return;
final CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
TextPaint textPaint = getPaint();
textPaint.getTextBounds(text.toString(), 0, text.length(), textBounds);
} else {
textBounds.setEmpty();
}
final int width = getWidth() - (getPaddingLeft() + getPaddingRight());
final int height = getHeight() - (getPaddingTop() + getPaddingBottom());
final Drawable[] drawables = getCompoundDrawables();
if (drawables[LEFT] != null) {
drawables[LEFT].copyBounds(drawableBounds);
int leftOffset =
(width - (textBounds.width() + drawableBounds.width()) + getRightPaddingOffset()) / 2 - getCompoundDrawablePadding();
drawableBounds.offset(leftOffset, 0);
drawables[LEFT].setBounds(drawableBounds);
}
if (drawables[RIGHT] != null) {
drawables[RIGHT].copyBounds(drawableBounds);
int rightOffset =
((textBounds.width() + drawableBounds.width()) - width + getLeftPaddingOffset()) / 2 + getCompoundDrawablePadding();
drawableBounds.offset(rightOffset, 0);
drawables[RIGHT].setBounds(drawableBounds);
}
if (drawables[TOP] != null) {
drawables[TOP].copyBounds(drawableBounds);
int topOffset =
(height - (textBounds.height() + drawableBounds.height()) + getBottomPaddingOffset()) / 2 - getCompoundDrawablePadding();
drawableBounds.offset(topOffset, 0);
drawables[TOP].setBounds(drawableBounds);
}
}
}

cannot change background color of custom editText in android

i quoted custom editText because i want to draw lines in edittext
so i have this class
public class LinedEditText extends android.support.v7.widget.AppCompatEditText {
private Rect mRect;
private Paint mPaint;
private int COLOR;
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
SharedPreferences sh= PreferenceManager.getDefaultSharedPreferences(context);
String co=sh.getString("line_color", String.valueOf(R.color.blue_line));
mPaint.setColor(getResources().getColor(Integer.parseInt(co))); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
and i used this class in xml and in another class (MainActivity) without any problem
but when i try to change color pro grammatically is not change even if i change that in xml ,is not change
xml :
<my.app.haythamayyash.note.LinedEditText
android:layout_width="match_parent"
android:inputType="textMultiLine"
android:scrollbars="vertical"
android:gravity="top|start"
android:ems="10"
android:id="#+id/detail2"
android:layout_height="463dp"
android:backgroundTint="#android:color/transparent"
android:textColor="#android:color/black"
android:minHeight="510dp"/>
i try that by added android:background="#color/gray" but no thing changed
, and i try to change that in java by
ed.setBackgroundColor(Color.parseColor("#ffffff"));
and
ed.setBackgroundResource(R.color.gray);
but nothing change
i guess the issue in LinedEditText class because i changed other editText (not LinedEditText) and its work ..
how to change background color of this editText (programmatically) ??
Use this code in LinedEditText in onDraw() method for changing its color:
canvas.drawColor(Color.BLACK);
or by resources layout file you can do this like:
app:backgroundTint="#android:color/holo_red_dark"
Use this code that would help u , it help me
app:backgroundTint="#color/gray"

Trying to create an EditText with a Label without the label being moved

I'm trying to create an EditText that has a Label inside the EditText and I've been using the second suggestion of the accepted answer here.
I've managed to sort of get it working, but with a few problems. Here's how it currently looks:
That's fine, until you try and enter some text. Initially there is no margin between the label and the entered text:
Also, the label cannot be removed with backspace, but it does disappear when you enter enough text to push it off the left:
Here is the code for my custom EditText - almost the same as in the thread mentioned above:
public class KingdomSpasEditText extends EditText {
private int paddingLeft;
private String label = "";
public KingdomSpasEditText(Context context) {
super(context);
paddingLeft = getPaddingLeft();
}
public KingdomSpasEditText(Context context, AttributeSet attrs) {
super(context, attrs);
paddingLeft = getPaddingLeft();
label = attrs.getAttributeValue("http://www.kingdomspas.com/android/custom", "label");
if (label == null) {
label = "";
}
}
protected void onDraw(Canvas canvas) {
TextPaint textPaint = getPaint();
Rect size = new Rect();
textPaint.getTextBounds(label, 0, label.length(), size);
setPadding(paddingLeft + size.width(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
super.onDraw(canvas);
canvas.drawText(label, paddingLeft + size.left, size.bottom + getPaddingTop() + 50, textPaint);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
You may notice that in the drawText() I am adding 50 to the top padding. If I don't it looks like this:
This seems like a hack to me.
Here is the XML used to create each EditText - the only difference being the label. They are both in a TableRow which is, obviously, part of a TableLayout:
<com.kingdomspas.android.kingdomspasforms.formfields.KingdomSpasEditText
android:id="#+id/editText1" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:maxLines="1"
android:singleLine="true" android:background="#drawable/round"
android:paddingTop="2dp"
android:paddingLeft="8dp"
android:layout_weight="1"
kingdomspas:label="Test Text 1: ">
<requestFocus />
</com.kingdomspas.android.kingdomspasforms.formfields.KingdomSpasEditText>
OK, thanks for sticking with me. Here is a summary of my problems:
1) How can I create a margin between the label and the entered text?
2) How can I prevent the label disapearing of to the right?
3) Is there a better way of my label in the Edittext

TextView last line drawn in half

I have a TextView which height is dynamic. I want to set that the last line wouldnt be visible if it cannot be fully drawn.
screenshot
The reason why height is dynamic is that it is used in an AppWidget, and the size of an appwidget is not the same on different devices. Because of this you cannot measure the TextView or cannot use a subclass of a TextView.
link
This is what I use now basically. When I put a long text into the TextView the last line looks ugly.
<LinearLayout
android:id="#+id/widgetContent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="#+id/sg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
I've faced this problem recently and used another solution.
I'm setting the maxLines dynamically:
final TextView tv = ((TextView) myLayout.findViewById(R.id.mytextview));
tv.setText(value);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private int maxLines = -1;
#Override
public void onGlobalLayout() {
if (maxLines < 0 && tv.getHeight() > 0 && tv.getLineHeight() > 0) {
int height = tv.getHeight();
int lineHeight = tv.getLineHeight();
maxLines = height / lineHeight;
tv.setMaxLines(maxLines);
}
}
});
Try this
Paint paint = new Paint();
// set paint with all current text properties of the textview. like text size, face etc,
textPixelLenght = paint.measureText(mText);
int textviewWidth = mTextView.getMeasuredWidth();
int numberOfLines = textPixelLenght /textviewWidth;;
int textlineHeight = textview.getLineHeight ();
if (textlineheight * numberOfLines > textviewHeight) {
// text going beyond textviews height. Ellipsize the text using
// TextUtils.ellipsize(params);
}
You need to know the spacing of your font -
ie, if you know its 20 characters per line, and you know each full line is 10px high, then you should dynamically assign the text to the TextView:
String toAdd = "";
for (int i=0; i < textViewHeight; i+= textHeight)
toAdd += fullText.subString(i, i+charactersPerLine);
A solution to your problem would be to create a custom TextView and override the onDraw() method of the TextView. There you could manage the way this TextView handles this case.
Using the height of the font dynamically change the height of the view, ie
If your text is 10px high, your TextView can only be 10, 20, 30px high. This doesn't require you to have any particular style of font.
This solution doubles the measure pass, but because this is a text view with no children the impact shouldn't be too great. This also won't help with a widget, as it's a custom text view. Sorry!
public class ExactWrappingTextView extends TextView{
public ExactWrappingTextView(Context context) {
super(context);
}
public ExactWrappingTextView(Context context, AttributeSet attrs){
super(context, attrs);
}
public ExactWrappingTextView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
private int measureHeight;
private int extraSpace;
//This doubles the measure pass for this view... but we have to know how
//high it would have been before we can pull off the right amount
public void onMeasure(int width, int height){
super.onMeasure(width, height);
extraSpace = (getMeasuredHeight() - (getPaddingTop() + getPaddingBottom())) % getLineHeight();
measureHeight = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - extraSpace, MeasureSpec.AT_MOST);
super.onMeasure(width, measureHeight);
}
}

Text with gradient in Android

How would I extend TextView to allow the drawing of text with a gradient effect?
TextView secondTextView = new TextView(this);
Shader textShader=new LinearGradient(0, 0, 0, 20,
new int[]{Color.GREEN,Color.BLUE},
new float[]{0, 1}, TileMode.CLAMP);
secondTextView.getPaint().setShader(textShader);
I have used the top answer(#Taras) with a gradient of 5 colors, but there is a problem: the textView looks like that I have put a white cover on it. Here is my code and the screenshot.
textView = (TextView) findViewById(R.id.main_tv);
textView.setText("Tianjin, China".toUpperCase());
TextPaint paint = textView.getPaint();
float width = paint.measureText("Tianjin, China");
Shader textShader = new LinearGradient(0, 0, width, textView.getTextSize(),
new int[]{
Color.parseColor("#F97C3C"),
Color.parseColor("#FDB54E"),
Color.parseColor("#64B678"),
Color.parseColor("#478AEA"),
Color.parseColor("#8446CC"),
}, null, Shader.TileMode.CLAMP);
textView.getPaint().setShader(textShader);
After many hours, I found out that I need to call textView.setTextColor() with the first color of the gradient. Then the screenshot:
Hope help someone!
It doesn't appear possible to extend TextView to draw text with a gradient. It is, however, possible to achieve this effect by creating a canvas and drawing on it. First we need to declare our custom UI element. In the initiation we need to create a subclass of Layout. In this case, we will use BoringLayout which only supports text with a single line.
Shader textShader=new LinearGradient(0, 0, 0, 20,
new int[]{bottom,top},
new float[]{0, 1}, TileMode.CLAMP);//Assumes bottom and top are colors defined above
textPaint.setTextSize(textSize);
textPaint.setShader(textShader);
BoringLayout.Metrics boringMetrics=BoringLayout.isBoring(text, textPaint);
boringLayout=new BoringLayout(text, textPaint, 0, Layout.Alignment.ALIGN_CENTER,
0.0f, 0.0f, boringMetrics, false);
We then override onMeasure and onDraw:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
setMeasuredDimension((int) textPaint.measureText(text), (int) textPaint.getFontSpacing());
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
boringLayout.draw(canvas);
}
Our implementation of onDraw is at this point quite lazy (it completely ignores the measurement specs!, but so long as you guarantee that the view is given sufficent space, it should work okay.
Alternatively, it would be possible to inherit from a Canvas and override the onPaint method. If this is done, then unfortunately the anchor for text being drawn will always be on the bottom so we have to add -textPaint.getFontMetricsInt().ascent() to our y coordinate.
Here it is with multiline support as a one liner. This should work for Buttons too.
Shader shader = new LinearGradient(0,0,0,textView.getLineHeight(),
startColor, endColor, Shader.TileMode.REPEAT);
textView.getPaint().setShader(shader);
I've rolled up a library that encompasses both of these methods. You can create GradientTextView in XML or just use GradientTextView.setGradient(TextView textView...) to do it on a regular TextView object.
https://github.com/koush/Widgets
A simple but somewhat limited solution would be to use these attributes:
android:fadingEdge="horizontal"
android:scrollHorizontally="true"
I have used it on textfields where I want them to fade out if they get too long.
Kotlin + coroutines version.
Extension for setting vertical gradient:
private fun TextView.setGradientTextColor(vararg colorRes: Int) {
val floatArray = ArrayList<Float>(colorRes.size)
for (i in colorRes.indices) {
floatArray.add(i, i.toFloat() / (colorRes.size - 1))
}
val textShader: Shader = LinearGradient(
0f,
0f,
0f,
this.height.toFloat(),
colorRes.map { ContextCompat.getColor(requireContext(), it) }.toIntArray(),
floatArray.toFloatArray(),
TileMode.CLAMP
)
this.paint.shader = textShader
}
Suspend extension. You need to wait for the view to change its height.
suspend fun View.awaitLayoutChange() = suspendCancellableCoroutine<Unit> { cont ->
val listener = object : View.OnLayoutChangeListener {
override fun onLayoutChange(
view: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
) {
view?.removeOnLayoutChangeListener(this)
cont.resumeWith(Result.success(Unit))
}
}
addOnLayoutChangeListener(listener)
cont.invokeOnCancellation { removeOnLayoutChangeListener(listener) }
}
And usage:
lifecycle.coroutineScope.launch {
binding.tvAmount.text = "Dumb text"
binding.tvAmount.awaitLayoutChange()
binding.tvAmount.setGradientTextColor(
R.color.yellow,
R.color.green
)
}
For Kotlin:
val paint: TextPaint = textView.paint
val width: Float = paint.measureText(holder.langs.text.toString())
val textShader: Shader = LinearGradient(0f, 0f, width, holder.langs.textSize, intArrayOf(
Color.parseColor("#8913FC"),
Color.parseColor("#00BFFC")), null, Shader.TileMode.CLAMP)
holder.langs.paint.shader = textShader
Here's my solved way. Implement with text span.
screenshot
class LinearGradientForegroundSpan extends CharacterStyle implements UpdateAppearance {
private int startColor;
private int endColor;
private int lineHeight;
public LinearGradientForegroundSpan(int startColor, int endColor, int lineHeight) {
this.startColor = startColor;
this.endColor = endColor;
this.lineHeight = lineHeight;
}
#Override
public void updateDrawState(TextPaint tp) {
tp.setShader(new LinearGradient(0, 0, 0, lineHeight,
startColor, endColor, Shader.TileMode.REPEAT));
}
}
Styled your gradient text.
SpannableString gradientText = new SpannableString("Gradient Text");
gradientText.setSpan(new LinearGradientForegroundSpan(Color.RED, Color.LTGRAY, textView.getLineHeight()),
0, gradientText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(gradientText);
sb.append(" Normal Text");
textView.setText(sb);
Here's a nice way to do it:
/**
* sets a vertical gradient on the textView's paint, so that on its onDraw method, it will use it.
*
* #param viewAlreadyHasSize
* set to true only if the textView already has a size
*/
public static void setVerticalGradientOnTextView(final TextView tv, final int positionsAndColorsResId,
final boolean viewAlreadyHasSize) {
final String[] positionsAndColors = tv.getContext().getResources().getStringArray(positionsAndColorsResId);
final int[] colors = new int[positionsAndColors.length];
float[] positions = new float[positionsAndColors.length];
for (int i = 0; i < positionsAndColors.length; ++i) {
final String positionAndColors = positionsAndColors[i];
final int delimeterPos = positionAndColors.lastIndexOf(':');
if (delimeterPos == -1 || positions == null) {
positions = null;
colors[i] = Color.parseColor(positionAndColors);
} else {
positions[i] = Float.parseFloat(positionAndColors.substring(0, delimeterPos));
String colorStr = positionAndColors.substring(delimeterPos + 1);
if (colorStr.startsWith("0x"))
colorStr = '#' + colorStr.substring(2);
else if (!colorStr.startsWith("#"))
colorStr = '#' + colorStr;
colors[i] = Color.parseColor(colorStr);
}
}
setVerticalGradientOnTextView(tv, colors, positions, viewAlreadyHasSize);
}
/**
* sets a vertical gradient on the textView's paint, so that on its onDraw method, it will use it. <br/>
*
* #param colors
* the colors to use. at least one should exist.
* #param tv
* the textView to set the gradient on it
* #param positions
* where to put each color (fraction, max is 1). if null, colors are spread evenly .
* #param viewAlreadyHasSize
* set to true only if the textView already has a size
*/
public static void setVerticalGradientOnTextView(final TextView tv, final int[] colors, final float[] positions,
final boolean viewAlreadyHasSize) {
final Runnable runnable = new Runnable() {
#Override
public void run() {
final TileMode tile_mode = TileMode.CLAMP;
final int height = tv.getHeight();
final LinearGradient lin_grad = new LinearGradient(0, 0, 0, height, colors, positions, tile_mode);
final Shader shader_gradient = lin_grad;
tv.getPaint().setShader(shader_gradient);
}
};
if (viewAlreadyHasSize)
runnable.run();
else
runJustBeforeBeingDrawn(tv, runnable);
}
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
runnable.run();
return true;
}
};
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
Also, if you wish to use a bitmap of the gradient, instead or a real one, use:
/**
* sets an image for the textView <br/>
* NOTE: this function must be called after you have the view have its height figured out <br/>
*/
public static void setBitmapOnTextView(final TextView tv, final Bitmap bitmap) {
final TileMode tile_mode = TileMode.CLAMP;
final int height = tv.getHeight();
final int width = tv.getWidth();
final Bitmap temp = Bitmap.createScaledBitmap(bitmap, width, height, true);
final BitmapShader bitmapShader = new BitmapShader(temp, tile_mode, tile_mode);
tv.getPaint().setShader(bitmapShader);
}
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
I have combined the answers from this thread and made a lightweight library. You can use it with gradle implementation, or simply use the files needed by adding it to your source.
https://github.com/veeyaarVR/SuperGradientTextView
The solution that worked for me is to apply a text color before applying any shaders. As the author of the question posted:
After many hours, I found out that I need to call textView.setTextColor() with the first color of the gradient. Then the screenshot:
What works is to have, for instance, a white color setup as text color in the first place. Then we can apply the shader, and it will be applied on top of the white so we will get the desired gradient color.
Here is an example for linearlayout, you can use this example for textview too, and in the source code there wont be gradient coding, you get the source code and add the code from that site itself - http://android-codes-examples.blogspot.com/2011/07/design-linearlayout-or-textview-and-any.html
I've found the way to do this without the TextView class extension.
class MainActivity : AppCompatActivity() {
private val textGradientOnGlobalLayoutListener = object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
textGradient.paint.shader = LinearGradient(0f, 0f,
textGradient.width.toFloat(),
textGradient.height.toFloat(),
color0, color1, Shader.TileMode.CLAMP)
textGradient.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
private val textGradient by lazy {
findViewById<TextView>(R.id.text_gradient)
}
private val color0 by lazy {
ContextCompat.getColor(applicationContext, R.color.purple_200)
}
private val color1 by lazy {
ContextCompat.getColor(applicationContext, R.color.teal_200)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textGradient.viewTreeObserver.addOnGlobalLayoutListener(textGradientOnGlobalLayoutListener)
}
}
Try
import com.sanne.MultiColorTextView;
MultiColorTextView textview= new MultiColorTextView(this);
textview.setText("SOME TEXT");
textview.setTextColor(/*INT ARRAY WITH YOUR COLOURS*/ );
The program sets a gradient colour across the textview and you can also set separate colours for particular text using
multiColorTextView.colorAll("A word");
MutliColorTextView from https://www.github.com/sanneemmanuel/MultiColorTextView

Categories

Resources