Подтвердить что ты не робот

Текст с градиентом в Android

Как мне расширить TextView, чтобы разрешить рисование текста с эффектом градиента?

4b9b3361

Ответ 1

Невозможно расширить TextView для рисования текста с помощью градиента. Однако достичь этого эффекта можно, создав холст и опираясь на него. Сначала нам нужно объявить наш пользовательский элемент интерфейса. В начале нам нужно создать подкласс Layout. В этом случае мы будем использовать BoringLayout, который поддерживает только текст с одной строкой.

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);

Затем мы переопределяем onMeasure и 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);
}

Наша реализация onDraw на данный момент довольно ленива (она полностью игнорирует спецификации измерений!, но пока вы гарантируете, что представление дано достаточное пространство, оно должно работать нормально.

В качестве альтернативы можно было бы наследовать от Canvas и переопределить метод onPaint. Если это будет сделано, то, к сожалению, якорь для рисованного текста всегда будет внизу, поэтому мы должны добавить -textPaint.getFontMetricsInt().ascent() к нашей координате y.

Ответ 2

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);

Ответ 3

Я использовал верхний ответ (@Taras) с градиентом 5 цветов, но есть проблема: textView выглядит так, как будто я наложил на него белую обложку. Вот мой код и скриншот.

        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);

enter image description here

Через много часов я обнаружил, что мне нужно вызвать textView.setTextColor() с первым цветом градиента. Тогда скриншот:

enter image description here

Надеюсь помочь кому-нибудь!

Ответ 4

Я свернул библиотеку, которая охватывает оба этих метода. Вы можете создать GradientTextView в XML или просто использовать GradientTextView.setGradient(TextView textView...), чтобы сделать это на обычном объекте TextView.

https://github.com/koush/Widgets

Ответ 5

Вот он, с поддержкой нескольких строк в качестве одного лайнера. Это должно работать и для кнопок.

Shader shader = new LinearGradient(0,0,0,textView.getLineHeight(),
                                  startColor, endColor, Shader.TileMode.REPEAT);
textView.getPaint().setShader(shader);

Ответ 6

Простым, но несколько ограниченным решением было бы использовать эти атрибуты:

android:fadingEdge="horizontal"
android:scrollHorizontally="true"

Я использовал его на текстовых полях, где я хочу, чтобы они исчезли, если они слишком длинны.

Ответ 7

Вот хороший способ сделать это:

/**
 * sets a vertical gradient on the textView 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 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);
}

Также, если вы хотите использовать растровое изображение градиента вместо реального, используйте:

/**
 * 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);
}

РЕДАКТИРОВАТЬ: Альтернатива runJustBeforeBeingDrawn: fooobar.com/questions/35172/...

Ответ 8

Вот пример для linearlayout, вы можете также использовать этот пример для текстового просмотра, а в исходном коде не будет градиентного кодирования, вы получаете исходный код и добавляете код с этого сайта - http://android-codes-examples.blogspot.com/2011/07/design-linearlayout-or-textview-and-any.html