web-dev-qa-db-fra.com

Texte avec dégradé sous Android

Comment pourrais-je étendre TextView pour permettre de dessiner du texte avec un effet de dégradé?

54
Casebash

Il ne semble pas possible d'étendre TextView pour dessiner du texte avec un dégradé. Il est toutefois possible d’atteindre cet effet en créant un canevas et en le dessinant. Nous devons d’abord déclarer notre élément d’interface utilisateur personnalisé . Au début, nous devons créer une sous-classe de Layout . Dans ce cas, nous utiliserons BoringLayout qui ne supporte que le texte avec une seule ligne.

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

Nous substituons ensuite onMeasure et 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);
}

Notre implémentation de onDraw est à ce stade assez paresseuse (elle ignore complètement les spécifications de mesure !, mais si vous garantissez que la vue dispose d'un espace suffisant, elle devrait fonctionner correctement.

Sinon, il serait possible d'hériter d'une Canvas et de remplacer la méthode onPaint. Si cela est fait, malheureusement, l'ancre pour le texte en cours de dessin sera toujours en bas, nous devons donc ajouter -textPaint.getFontMetricsInt().ascent() à notre coordonnée y.

18
Casebash
 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);
115
Taras

J'ai rassemblé une bibliothèque qui englobe ces deux méthodes. Vous pouvez créer GradientTextView en XML ou simplement utiliser GradientTextView.setGradient (TextView textView ...) pour le faire sur un objet TextView normal.

https://github.com/koush/Widgets

10
koush

J'ai utilisé la réponse supérieure (@Taras) avec un dégradé de 5 couleurs, mais il y a un problème: le textView ressemble à cela, j'ai mis une couverture blanche dessus. Voici mon code et la capture d'écran.

        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

Après de nombreuses heures, j'ai découvert que je devais appeler textView.setTextColor() avec la première couleur du dégradé. Puis la capture d'écran:

 enter image description here

J'espère aider quelqu'un!

6
hanswim

Une solution simple mais quelque peu limitée serait d'utiliser ces attributs:

Android:fadingEdge="horizontal"
Android:scrollHorizontally="true"

Je l'ai utilisé sur des champs de texte où je veux qu'ils disparaissent s'ils deviennent trop longs.

3
pgsandstrom

Voici une bonne façon de le faire:

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

De plus, si vous souhaitez utiliser un bitmap du dégradé, ou un vrai, utilisez:

/**
 * 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 à runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126

1
android developer

Voici un exemple pour linearlayout, vous pouvez aussi utiliser cet exemple pour textview, et dans le code source, il n'y aura pas de codage en dégradé, vous obtenez le code source et ajoutez le code de ce site lui-même - http: // codes-Android -examples.blogspot.com/2011/07/design-linearlayout-or-textview-and-any.html

0
Android