5

Following code will set gradient on textview (not background, but text itself). But I need to change angle of this gradient, how to do it?

Shader textShader = new LinearGradient(0, 0, 0, textView.getPaint().getTextSize(),
        new int[]{context.getResources().getColor(R.color.color1), context.getResources().getColor(R.color.color2)},
        new float[]{0, 1}, Shader.TileMode.CLAMP);
textView.getPaint().setShader(textShader);

Thank you in advance.

Michalsx
  • 3,446
  • 5
  • 33
  • 46

3 Answers3

2
final TextView myTextView = findViewById(R.id.my_text_view);

myTextView.post(new Runnable() 
    {

         @Override

            public void run() {

                int length = textView.getMeasuredWidth();

            float angle = 45; // specify angle in degrees here

            Shader textShader = new LinearGradient(0, 0, (int) (Math.sin(Math.PI * angle / 180) * length), 
                                    (int) (Math.cos(Math.PI * angle / 180) * length),

                                        new int[]{Color.BLUE, Color.GREEN, Color.GREEN, Color.BLUE, Color.RED, Color.GREEN, Color.BLUE, Color.RED},

    null, 
        Shader.TileMode.CLAMP);

        myTextView.getPaint().setShader(textShader);

            textView.invalidate();

            }

    });

result

  • From Review:  Hi, please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Feb 04 '20 at 09:30
1

According to this answer, I modified your code. Try this:

double angleInRadians = Math.toRadians(45);
double length = textView.getPaint().getTextSize();

double endX = Math.sin(angleInRadians) * length;
double endY = Math.cos(angleInRadians) * length;

Shader textShader = new LinearGradient(0, 0, endX, endY,
        new int[]{context.getResources().getColor(R.color.color1), context.getResources().getColor(R.color.color2)},
        new float[]{0, 1}, Shader.TileMode.CLAMP);
textView.getPaint().setShader(textShader);


Update:
I'm ashamed to say that my above answer is not correct. These answers (mayank1513 answer and Chaudhari Sachin answer) are the same and they are good but they have few bugs:
1. They don't work correctly for agnles more than 90 degrees. For exmaple if you test them with 180 degrees, then you won't see any gradient, because these solutions are using (0,0) point as rotation center.
2. If you have a TextView with width or height more or less than its text width or height, then gradient doesn't render correctly. For example you can test it by a TextView with match_parent width and height and centered Hello text or with an enabled scrolling small TextView and long text.
I developed new soltuion. It calcuates text bound first and then rotates gradient line around center of this bound.

textView.post(new Runnable() {
    @Override
    public void run() {

        final Rect textBound = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);

        final Layout layout = textView.getLayout();
        for(int i = 0; i < textView.getLineCount(); i++) {
            float left = layout.getLineLeft(i);
            float right = layout.getLineRight(i);
            if(left < textBound.left) textBound.left = (int)left;
            if(right > textBound.right) textBound.right = (int)right;
        }
        textBound.top = layout.getLineTop(0);
        textBound.bottom = layout.getLineBottom(textView.getLineCount() - 1);
        if(textView.getIncludeFontPadding()) {
            Paint.FontMetrics fontMetrics = textView.getPaint().getFontMetrics();
            textBound.top += (fontMetrics.ascent - fontMetrics.top);
            textBound.bottom -= (fontMetrics.bottom - fontMetrics.descent);
        }

        double angleInRadians = Math.toRadians(45);

        double r = Math.sqrt(Math.pow(textBound.bottom - textBound.top, 2) +
                Math.pow(textBound.right - textBound.left, 2)) / 2;

        float centerX = textBound.left + (textBound.right - textBound.left) / 2;
        float centerY = textBound.top + (textBound.bottom - textBound.top) / 2;

        float startX = (float)Math.max(textBound.left, Math.min(textBound.right, centerX - r * Math.cos(angleInRadians)));
        float startY = (float)Math.min(textBound.bottom, Math.max(textBound.top, centerY - r * Math.sin(angleInRadians)));

        float endX = (float)Math.max(textBound.left, Math.min(textBound.right, centerX + r * Math.cos(angleInRadians)));
        float endY = (float)Math.min(textBound.bottom, Math.max(textBound.top, centerY + r * Math.sin(angleInRadians)));

        Shader textShader = new LinearGradient(startX, startY, endX, endY,
                new int[]{context.getResources().getColor(R.color.color1),
                        context.getResources().getColor(R.color.color2)},
                new float[]{0, 1}, Shader.TileMode.CLAMP);

        textView.setTextColor(Color.WHITE);
        textView.getPaint().setShader(textShader);
    }
});
Mir Milad Hosseiny
  • 2,769
  • 15
  • 19
0

Working code

final double angleInRadians = Math.toRadians(45);
final TextView textView = findViewById(R.id.app_name);
    textView.post(new Runnable() {
        @Override
        public void run() {
            double length = textView.getMeasuredWidth();

            double endX = Math.sin(angleInRadians) * length;
            double endY = Math.cos(angleInRadians) * length;

            Shader textShader = new LinearGradient(0, 0, (int) endX, (int) endY,
                    new int[]{Color.BLUE, Color.GREEN, Color.GREEN, Color.BLUE, Color.RED, Color.GREEN, Color.BLUE, Color.RED},
                    null, Shader.TileMode.CLAMP);
            textView.getPaint().setShader(textShader);
            textView.invalidate();
        }
    });

This sets gradient with specified angle.

Mayank Kumar Chaudhari
  • 16,027
  • 10
  • 55
  • 122