2010-12-03 3 views
4

Je travaille actuellement sur l'extension d'un TextView, en ajoutant un contour autour du texte. Jusqu'ici, le seul problème que j'ai eu est mon incapacité à positionner correctement le "contour" derrière un texte. Si je code la classe étendu comme celui décrit ci-dessous, je reçois une étiquette qui ressemble à ceci:Comment dessiner correctement du texte dans une classe étendue pour TextView?

Note: la capture d'écran ci-dessus, je mets la couleur de remplissage blanc et la couleur de la course à noir.

Qu'est-ce que je fais mal?

public class OutlinedTextView extends TextView { 
    /* =========================================================== 
    * Constants 
    * =========================================================== */ 
    private static final float OUTLINE_PROPORTION = 0.1f; 

    /* =========================================================== 
    * Members 
    * =========================================================== */ 
    private final Paint mStrokePaint = new Paint(); 
    private int mOutlineColor = Color.TRANSPARENT; 

    /* =========================================================== 
    * Constructors 
    * =========================================================== */ 
    public OutlinedTextView(Context context) { 
     super(context); 
     this.setupPaint(); 
    } 
    public OutlinedTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     this.setupPaint(); 
     this.setupAttributes(context, attrs); 
    } 
    public OutlinedTextView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     this.setupPaint(); 
     this.setupAttributes(context, attrs); 
    } 

    /* =========================================================== 
    * Overrides 
    * =========================================================== */ 
    @Override 
    protected void onDraw(Canvas canvas) { 
     // Get the text to print 
     final float textSize = super.getTextSize(); 
     final String text = super.getText().toString(); 

     // setup stroke 
     mStrokePaint.setColor(mOutlineColor); 
     mStrokePaint.setStrokeWidth(textSize * OUTLINE_PROPORTION); 
     mStrokePaint.setTextSize(textSize); 
     mStrokePaint.setFlags(super.getPaintFlags()); 
     mStrokePaint.setTypeface(super.getTypeface()); 

     // Figure out the drawing coordinates 
     //mStrokePaint.getTextBounds(text, 0, text.length(), mTextBounds); 

     // draw everything 
     canvas.drawText(text, 
       super.getWidth() * 0.5f, super.getBottom() * 0.5f, 
       mStrokePaint); 
     super.onDraw(canvas); 
    } 

    /* =========================================================== 
    * Private/Protected Methods 
    * =========================================================== */ 
    private final void setupPaint() { 
     mStrokePaint.setAntiAlias(true); 
     mStrokePaint.setStyle(Paint.Style.STROKE); 
     mStrokePaint.setTextAlign(Paint.Align.CENTER); 
    } 
    private final void setupAttributes(Context context, AttributeSet attrs) { 
     final TypedArray array = context.obtainStyledAttributes(attrs, 
       R.styleable.OutlinedTextView); 
     mOutlineColor = array.getColor(
       R.styleable.OutlinedTextView_outlineColor, 0x00000000); 
     array.recycle(); 

     // Force this text label to be centered 
     super.setGravity(Gravity.CENTER_HORIZONTAL); 
    } 
} 
+0

J'ai oublié de mentionner la disposition XML actuelle en ce moment. Bien que je ne l'ai pas encore sur moi, fondamentalement j'essaye de centrer le TextView verticalement et horizontalement au-dessus d'un ImageButton. – Japtar

Répondre

4

Bah, c'était stupide de ma part. J'ai juste besoin de changer-up en commentaire ligne:

super.getPaint().getTextBounds(text, 0, text.length(), mTextBounds); 

En outre, pour rendre réellement le texte, je dois en moyenne la hauteur de ce point de vue et la hauteur du texte:

// draw everything 
canvas.drawText(text, 
    super.getWidth() * 0.5f, (super.getHeight() + mTextBounds.height()) * 0.5f, 
    mStrokePaint); 

L'ensemble Code se lit maintenant comme suit:

public class OutlinedTextView extends TextView { 
    /* =========================================================== 
    * Constants 
    * =========================================================== */ 
    private static final float OUTLINE_PROPORTION = 0.1f; 

    /* =========================================================== 
    * Members 
    * =========================================================== */ 
    private final Paint mStrokePaint = new Paint(); 
    private final Rect mTextBounds = new Rect(); 
    private int mOutlineColor = Color.TRANSPARENT; 

    /* =========================================================== 
    * Constructors 
    * =========================================================== */ 
    public OutlinedTextView(Context context) { 
     super(context); 
     this.setupPaint(); 
    } 
    public OutlinedTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     this.setupPaint(); 
     this.setupAttributes(context, attrs); 
    } 
    public OutlinedTextView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     this.setupPaint(); 
     this.setupAttributes(context, attrs); 
    } 

    /* =========================================================== 
    * Overrides 
    * =========================================================== */ 
    @Override 
    protected void onDraw(Canvas canvas) { 
     // Get the text to print 
     final float textSize = super.getTextSize(); 
     final String text = super.getText().toString(); 

     // setup stroke 
     mStrokePaint.setColor(mOutlineColor); 
     mStrokePaint.setStrokeWidth(textSize * OUTLINE_PROPORTION); 
     mStrokePaint.setTextSize(textSize); 
     mStrokePaint.setFlags(super.getPaintFlags()); 
     mStrokePaint.setTypeface(super.getTypeface()); 

     // Figure out the drawing coordinates 
     super.getPaint().getTextBounds(text, 0, text.length(), mTextBounds); 

     // draw everything 
     canvas.drawText(text, 
       super.getWidth() * 0.5f, (super.getHeight() + mTextBounds.height()) * 0.5f, 
       mStrokePaint); 
     super.onDraw(canvas); 
    } 

    /* =========================================================== 
    * Private/Protected Methods 
    * =========================================================== */ 
    private final void setupPaint() { 
     mStrokePaint.setAntiAlias(true); 
     mStrokePaint.setStyle(Paint.Style.STROKE); 
     mStrokePaint.setTextAlign(Paint.Align.CENTER); 
    } 
    private final void setupAttributes(Context context, AttributeSet attrs) { 
     final TypedArray array = context.obtainStyledAttributes(attrs, 
       R.styleable.OutlinedTextView); 
     mOutlineColor = array.getColor(
       R.styleable.OutlinedTextView_outlineColor, 0x00000000); 
     array.recycle(); 

     // Force this text label to be centered 
     super.setGravity(Gravity.CENTER_HORIZONTAL); 
    } 
} 
+0

Dans l'espoir que cela aiderait quelqu'un, vous n'avez pas besoin de calculer la position absolue de l'écran pour déterminer où dessiner le texte. OnDraw dessine réellement par rapport aux limites de la vue qu'il étend. Ainsi, pour centrer un trait de texte sur une vue, tout ce dont j'avais besoin était le centre horizontal de la vue et le centre vertical, compensés par la hauteur du texte (pour une raison quelconque, canvas.drawText a une option pour aligner horizontalement le texte , mais pas pour l'alignement vertical). – Japtar

0

Il sont quelques attributs dans la classe TextView, comme android:shadowColor, android:shadowDx, android:shadowDy et android:shadowRadius. Il me semble qu'ils font la même chose que vous voulez mettre en œuvre. Alors peut-être que vous devriez essayer un simple TextView au début.

+0

L'ombre du texte ne va pas être la même chose que Japtar essaie d'accomplir.Je suis en fait très intéressé par une solution pour cela aussi. Caresser le texte (avec une ombre portée, en fait) serait très utile! – kcoppock

+0

Oui, j'ai essayé d'ajouter les ombres avant. Comme l'a dit kcoppck, cependant, il n'a pas atteint ce que je voulais. Bonne réponse, néanmoins. – Japtar

+0

J'utilise des ombres pour esquisser dans mon projet. C'est dommage que ça ne t'aide pas. – Michael

1

J'ai essayé de le faire fonctionner pendant un certain temps et j'ai une solution, mais c'est pour un cas spécial seulement! Il est possible d'obtenir l'objet Layout utilisé à l'intérieur du TextView pour dessiner du texte. Vous pouvez créer une copie de cet objet et l'utiliser dans la méthode onDraw(Canvas).

final Layout originalLayout = super.getLayout(); 
    final Layout layout = new StaticLayout(text, mStrokePaint, 
    originalLayout.getWidth(), originalLayout.getAlignment(), 
    originalLayout.getSpacingMultiplier(), originalLayout.getSpacingAdd(), true); 

    canvas.save(); 
    canvas.translate(layout.getLineWidth(0) * 0.5f, 0.0f); 
    layout.draw(canvas); 
    canvas.restore(); 

Mais je suis sûr que ce n'est pas un bon moyen de dessiner des contours. Je ne sais pas comment suivre les modifications dans un objet TextView.getLayout(). En outre, il ne fonctionne pas pour les lignes multi-lignes TextView et différentes gravités. Et finalement ce code a de très mauvaises performances car il alloue un objet Layout à chaque tirage. Je ne comprends pas exactement comment cela fonctionne, donc je préfère ne pas l'utiliser.

+0

+1 pour l'effort, cependant. : O – kcoppock

+0

Même chose, vous obtenez mes points de respect :-). – Japtar

3

J'ai trimer avec certains de ces exemples pour un certain temps, comme ne semblait aligner tout à fait raison, et une fois que je suis finalement une poignée sur ce qui se passe avec t poste et de mettre mon chapeau sur les maths j'ai changé onDraw pour que ce soit ce qui suit et il se repose parfaitement, peu importe la taille du texte ou la taille et la forme, il est vue est ... contenant

@Override 
protected void onDraw(Canvas canvas) { 
    if (!isInEditMode()){ 
     // Get the text to print 
     final float textSize = super.getTextSize(); 
     final String text = super.getText().toString(); 

     // setup stroke 
     mStrokePaint.setColor(mOutlineColor); 
     mStrokePaint.setStrokeWidth(textSize * mOutlineSize); 
     mStrokePaint.setTextSize(textSize); 
     mStrokePaint.setFlags(super.getPaintFlags()); 
     mStrokePaint.setTypeface(super.getTypeface()); 

     // draw everything 
     canvas.drawText(text, 
       (this.getWidth()-mStrokePaint.measureText(text))/2, this.getBaseline(), 
       mStrokePaint); 
    } 
    super.onDraw(canvas); 
} 

se sont révélées être beaucoup moins mathématique et Rect calculant que beaucoup de solutions utilisées aussi. Edit: J'ai oublié de mentionner que je copie le texte du super dans l'initialisation et ne le force pas au centre. La position drawText calculée ici est toujours la position correctement centrée pour le texte contourné.

+0

Savez-vous si cela va se positionner correctement si le texte est aligné à droite, aligné au centre, aligné au haut, etc.? – Japtar

+0

Il va certainement faire face aux alignements haut et bas, puisque getBaseline est la bonne position de base calculée pour le texte 'réel'. La position x ici va toujours être centrée, mais vous pouvez facilement changer l'alignement et manipuler les maths car c'est en fait assez simple une fois que vous avez trouvé les bonnes valeurs à lire. Aligné à gauche (ignorant le remplissage) serait 0 et aligné à droite serait getWidth() - measureText (texte). Vous pouvez lire le remplissage à partir de la vue de texte pour la gauche et la droite aussi si cela est nécessaire. Je suis sûr que je vais faire face à tout cela un jour bientôt pour un autre projet. – Zulaxia

+0

Cela a du sens. Je peux certainement faire face au manque d'alignement horizontal, puisque je veux un alignement central, de toute façon. Je connaissais 'getBaseline()', mais je n'ai jamais pensé à l'utiliser pour une raison quelconque. Merci de me le rappeler. – Japtar

Questions connexes