Existe-t-il un moyen simple d’avoir du texte en noir? J'ai des textviews de couleurs différentes, mais certaines couleurs n'apparaissent pas aussi bien sur mon arrière-plan. Je me demandais donc s'il existait un moyen facile d'obtenir un contour noir ou autre chose qui ferait l'affaire. Je préférerais ne pas avoir à créer une vue personnalisée et à créer un canevas, etc.
Vous pouvez placer une ombre derrière le texte, ce qui peut souvent améliorer la lisibilité. Essayez d’essayer avec 50% d’ombres noires translucides sur votre texte vert. Vous trouverez des détails sur la procédure à suivre ici: Android - shadow on text?
Pour vraiment ajouter un trait autour du texte, vous devez faire quelque chose d'un peu plus compliqué, comme ceci: Comment dessinez-vous du texte avec une bordure sur un MapView sous Android?
Donc, un peu tard, mais MagicTextView fera les contours de texte, entre autres.
<com.qwerjk.better_text.MagicTextView
xmlns:qwerjk="http://schemas.Android.com/apk/res/com.qwerjk.better_text"
Android:textSize="78dp"
Android:textColor="#ff333333"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
qwerjk:strokeColor="#FFff0000"
qwerjk:strokeJoinStyle="miter"
qwerjk:strokeWidth="5"
Android:text="Magic" />
Note: J'ai fait ceci, et j'en publie plus pour le bien des futurs voyageurs que l'OP. C'est du spam à la limite, mais être sur le sujet, peut-être acceptable?
l’effet de contour peut être obtenu à l’aide de l’ombre dans TextView:
Android:shadowColor="#000000"
Android:shadowDx="1.5"
Android:shadowDy="1.3"
Android:shadowRadius="1.6"
Android:text="CCC"
Android:textAllCaps="true"
Android:textColor="@Android:color/white"
Le cadre prend en charge text-shadow, mais pas text-outline. Mais il y a un truc: l'ombre est quelque chose de translucide qui s'estompe. Redessine une ombre quelques temps et tout l'alpha sera résumé et un résultat est un contour.
Une implémentation très simple étend TextView et remplace la méthode draw. Chaque fois qu'un tirage est demandé, notre sous-classe effectue 5 à 10 dessins.
public class OutlineTextView extends TextView {
// Constructors
@Override
public void draw(Canvas canvas) {
for (int i = 0; i < 5; i++) {
super.draw(canvas);
}
}
}
<OutlineTextView
Android:shadowColor="#000"
Android:shadowRadius="3.0" />
C'est une question assez ancienne mais je ne vois toujours pas de réponses complètes. Je publie donc cette solution en espérant qu'une personne aux prises avec ce problème le trouvera utile. La solution la plus simple et la plus efficace consiste à remplacer la méthode onDraw de la classe TextView. La plupart des implémentations que j'ai vues utilisent la méthode drawText pour dessiner le trait, mais cette approche ne tient pas compte de tout l'alignement de mise en forme et de l'habillage du texte. Le résultat est souvent que le trait et le texte se retrouvent à différents endroits. L'approche suivante utilise super.onDraw pour dessiner à la fois le trait et le remplissage du texte afin que vous n'ayez pas à vous soucier du reste. Voici les étapes
Et appelez à nouveau la classe parente onDraw pour tracer le trait sur le texte précédemment rendu.
package com.example.widgets;
import Android.content.Context;
import Android.content.res.TypedArray;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.Typeface;
import Android.util.AttributeSet;
import Android.widget.Button;
public class StrokedTextView extends Button {
private static final int DEFAULT_STROKE_WIDTH = 0;
// fields
private int _strokeColor;
private float _strokeWidth;
// constructors
public StrokedTextView(Context context) {
this(context, null, 0);
}
public StrokedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if(attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.StrokedTextAttrs);
_strokeColor = a.getColor(R.styleable.StrokedTextAttrs_textStrokeColor,
getCurrentTextColor());
_strokeWidth = a.getFloat(R.styleable.StrokedTextAttrs_textStrokeWidth,
DEFAULT_STROKE_WIDTH);
a.recycle();
}
else {
_strokeColor = getCurrentTextColor();
_strokeWidth = DEFAULT_STROKE_WIDTH;
}
//convert values specified in dp in XML layout to
//px, otherwise stroke width would appear different
//on different screens
_strokeWidth = dpToPx(context, _strokeWidth);
}
// getters + setters
public void setStrokeColor(int color) {
_strokeColor = color;
}
public void setStrokeWidth(int width) {
_strokeWidth = width;
}
// overridden methods
@Override
protected void onDraw(Canvas canvas) {
if(_strokeWidth > 0) {
//set Paint to fill mode
Paint p = getPaint();
p.setStyle(Paint.Style.FILL);
//draw the fill part of text
super.onDraw(canvas);
//save the text color
int currentTextColor = getCurrentTextColor();
//set Paint to stroke mode and specify
//stroke color and width
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(_strokeWidth);
setTextColor(_strokeColor);
//draw text stroke
super.onDraw(canvas);
//revert the color back to the one
//initially specified
setTextColor(currentTextColor);
} else {
super.onDraw(canvas);
}
}
/**
* Convenience method to convert density independent pixel(dp) value
* into device display specific pixel value.
* @param context Context to access device specific display metrics
* @param dp density independent pixel value
* @return device specific pixel value.
*/
public static int dpToPx(Context context, float dp)
{
final float scale= context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
C'est tout. Cette classe utilise des attributs XML personnalisés pour permettre de spécifier la couleur et la largeur du trait à partir des fichiers de présentation XML. Par conséquent, vous devez ajouter ces attributs dans votre fichier attr.xml dans le sous-dossier 'valeurs' sous le dossier 'res'. Copiez et collez le texte suivant dans votre fichier attr.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="StrokedTextAttrs">
<attr name="textStrokeColor" format="color"/>
<attr name="textStrokeWidth" format="float"/>
</declare-styleable>
</resources>
Une fois que vous avez terminé, vous pouvez utiliser la classe personnalisée StrokedTextView dans vos fichiers de présentation XML et spécifier la couleur et la largeur du trait. Voici un exemple
<com.example.widgets.StrokedTextView
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Stroked text sample"
Android:textColor="@Android:color/white"
Android:textSize="25sp"
strokeAttrs:textStrokeColor="@Android:color/black"
strokeAttrs:textStrokeWidth="1.7" />
N'oubliez pas de remplacer le nom du paquet par le nom du paquet de votre projet. Ajoutez également l'espace de noms xmlns dans le fichier de disposition afin d'utiliser des attributs XML personnalisés. Vous pouvez ajouter la ligne suivante dans le nœud racine de votre fichier de mise en page.
xmlns:strokeAttrs="http://schemas.Android.com/apk/res-auto"
Je viens juste d'essayer de comprendre comment faire cela et je ne pouvais pas trouver un bon guide en ligne mais je l'ai finalement compris. Comme Steve Pomeroy l'a suggéré, vous devez faire quelque chose de plus impliqué. Pour obtenir l'effet de texte souligné, vous dessinez le texte deux fois: une fois avec un contour épais, puis la deuxième fois, nous traçons le texte principal par dessus le contour. Toutefois, la tâche est facilitée car vous pouvez très facilement adapter l’un des exemples de code fournis avec le SDK, à savoir celui qui se trouve sous ce nom dans votre répertoire SDK: "/ samples/Android-/ApiDemos/src/com/exemple/Android. /apis/view/LabelView.Java ". Ce qui peut également être trouvé sur le site du développeur Android ici .
En fonction de ce que vous faites, il est très facile de voir qu'il vous suffira d'apporter des modifications mineures à ce code, par exemple de le changer pour qu'il s'étende de TextView, etc. Avant de découvrir cet exemple, j'avais oublié de remplacer onMeasure () (qui vous devez faire en plus de remplacer onDraw () comme indiqué dans le guide "Création de composants personnalisés" sur le site Web de développeur Android), ce qui explique en partie pourquoi j'ai eu des problèmes.
Une fois que vous avez fait cela, vous pouvez faire ce que j'ai fait:
public class TextViewOutline extends TextView {
private Paint mTextPaint;
private Paint mTextPaintOutline; //add another Paint attribute for your outline
...
//modify initTextViewOutline to setup the outline style
private void initTextViewOutline() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaintOutline = new Paint();
mTextPaintOutline.setAntiAlias(true);
mTextPaintOutline.setTextSize(16);
mTextPaintOutline.setColor(0xFF000000);
mTextPaintOutline.setStyle(Paint.Style.STROKE);
mTextPaintOutline.setStrokeWidth(4);
setPadding(3, 3, 3, 3);
}
...
//make sure to update other methods you've overridden to handle your new Paint object
...
//and finally draw the text, mAscent refers to a member attribute which had
//a value assigned to it in the measureHeight and Width methods
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
mTextPaintOutline);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
}
Ainsi, pour obtenir l'effet de texte souligné, vous dessinez le texte deux fois: une fois avec un contour épais et ensuite une seconde fois, nous traçons le texte principal par-dessus le contour.
Voici le truc que j'ai trouvé qui fonctionne mieux que le trait IMO de MagicTextView
@Override
protected void onDraw(Canvas pCanvas) {
int textColor = getTextColors().getDefaultColor();
setTextColor(mOutlineColor); // your stroke's color
getPaint().setStrokeWidth(10);
getPaint().setStyle(Paint.Style.STROKE);
super.onDraw(pCanvas);
setTextColor(textColor);
getPaint().setStrokeWidth(0);
getPaint().setStyle(Paint.Style.FILL);
super.onDraw(pCanvas);
}
J'ai écrit une classe pour effectuer du texte avec contour et prendre en charge tous les autres attributs et dessins d'une vue de texte normale.
il utilise essentiellement la super.onDraw(Canves canvas)
sur la TextView
mais dessine deux fois avec des styles différents.
j'espère que cela t'aides.
public class TextViewOutline extends TextView {
// constants
private static final int DEFAULT_OUTLINE_SIZE = 0;
private static final int DEFAULT_OUTLINE_COLOR = Color.TRANSPARENT;
// data
private int mOutlineSize;
private int mOutlineColor;
private int mTextColor;
private float mShadowRadius;
private float mShadowDx;
private float mShadowDy;
private int mShadowColor;
public TextViewOutline(Context context) {
this(context, null);
}
public TextViewOutline(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
private void setAttributes(AttributeSet attrs){
// set defaults
mOutlineSize = DEFAULT_OUTLINE_SIZE;
mOutlineColor = DEFAULT_OUTLINE_COLOR;
// text color
mTextColor = getCurrentTextColor();
if(attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.TextViewOutline);
// outline size
if (a.hasValue(R.styleable.TextViewOutline_outlineSize)) {
mOutlineSize = (int) a.getDimension(R.styleable.TextViewOutline_outlineSize, DEFAULT_OUTLINE_SIZE);
}
// outline color
if (a.hasValue(R.styleable.TextViewOutline_outlineColor)) {
mOutlineColor = a.getColor(R.styleable.TextViewOutline_outlineColor, DEFAULT_OUTLINE_COLOR);
}
// shadow (the reason we take shadow from attributes is because we use API level 15 and only from 16 we have the get methods for the shadow attributes)
if (a.hasValue(R.styleable.TextViewOutline_Android_shadowRadius)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowDx)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowDy)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowColor)) {
mShadowRadius = a.getFloat(R.styleable.TextViewOutline_Android_shadowRadius, 0);
mShadowDx = a.getFloat(R.styleable.TextViewOutline_Android_shadowDx, 0);
mShadowDy = a.getFloat(R.styleable.TextViewOutline_Android_shadowDy, 0);
mShadowColor = a.getColor(R.styleable.TextViewOutline_Android_shadowColor, Color.TRANSPARENT);
}
a.recycle();
}
PFLog.d("mOutlineSize = " + mOutlineSize);
PFLog.d("mOutlineColor = " + mOutlineColor);
}
private void setPaintToOutline(){
Paint paint = getPaint();
Paint.setStyle(Paint.Style.STROKE);
Paint.setStrokeWidth(mOutlineSize);
super.setTextColor(mOutlineColor);
super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
}
private void setPaintToRegular() {
Paint paint = getPaint();
Paint.setStyle(Paint.Style.FILL);
Paint.setStrokeWidth(0);
super.setTextColor(mTextColor);
super.setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setPaintToOutline();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
mTextColor = color;
}
@Override
public void setShadowLayer(float radius, float dx, float dy, int color) {
super.setShadowLayer(radius, dx, dy, color);
mShadowRadius = radius;
mShadowDx = dx;
mShadowDy = dy;
mShadowColor = color;
}
public void setOutlineSize(int size){
mOutlineSize = size;
}
public void setOutlineColor(int color){
mOutlineColor = color;
}
@Override
protected void onDraw(Canvas canvas) {
setPaintToOutline();
super.onDraw(canvas);
setPaintToRegular();
super.onDraw(canvas);
}
}
attr.xml
<declare-styleable name="TextViewOutline">
<attr name="outlineSize" format="dimension"/>
<attr name="outlineColor" format="color|reference"/>
<attr name="Android:shadowRadius"/>
<attr name="Android:shadowDx"/>
<attr name="Android:shadowDy"/>
<attr name="Android:shadowColor"/>
</declare-styleable>
Vous pouvez le faire par programme avec l'extrait ci-dessous . Cela fournit des lettres blanches sur fond noir:
textView.setTextColor(Color.WHITE);
textView.setShadowLayer(1.6f,1.5f,1.3f,Color.BLACK);
Les paramètres de la méthode sont radius, dx, dy, color. Vous pouvez les changer pour vos besoins spécifiques.
J'espère que j'aiderai quelqu'un qui crée TextView par programme sans l'avoir dans XML.
Bravo à la communauté stackOverflow!
Donc, vous voulez un trait autour de la vue texte? Malheureusement, il n'y a pas de moyen simple de le faire avec le style. Vous devrez créer une autre vue et placer votre vue de texte par-dessus, ce qui fera que la vue parent (celle au-dessus de celle-ci) sera plus grande de quelques pixels - cela devrait créer un contour.
Je veux ajouter une solution afin de résoudre le problème de performances. Par exemple, la réponse de @YGHM et de quelques autres effectue le travail, mais elle provoque un appel infini de onDraw
car setTextColor
appelle invalidate()
. Donc, pour le résoudre, vous devez également redéfinir invalidate()
et ajouter une variable isDrawing
que vous définissez sur true
, lorsque onDraw()
est en cours et dessiné d'un trait. invalidate retournera si la variable est true
.
override fun invalidate() {
if (isDrawing) return
super.invalidate()
}
Votre onDraw ressemblera à ceci:
override fun onDraw(canvas: Canvas) {
if (strokeWidth > 0) {
isDrawing = true
val textColor = textColors.defaultColor
setTextColor(strokeColor)
Paint.strokeWidth = strokeWidth
Paint.style = Paint.Style.STROKE
super.onDraw(canvas)
setTextColor(textColor)
Paint.strokeWidth = 0f
Paint.style = Paint.Style.FILL
isDrawing = false
super.onDraw(canvas)
} else {
super.onDraw(canvas)
}
}
J'ai créé une bibliothèque basée sur la réponse de Nouman Hanif avec quelques ajouts. Par exemple, résoudre un problème qui provoquait une boucle infinie indirecte sur les appels View.invalidate ().
OTOH, la bibliothèque prend également en charge le texte décrit dans les widgets EditText, car c’était mon objectif principal et qu’il fallait un peu plus de travail que TextView.
Voici le lien vers ma bibliothèque: https://github.com/biomorgoth/Android-outline-textview
Merci à Nouman Hanif pour l’idée initiale sur la solution!
J'ai trouvé un moyen simple de contourner la vue sans héritage de TextView . J'avais écrit une bibliothèque simple qui utilise Spannable d'Android pour dessiner le texte . Cette solution permet de ne dessiner qu'une partie du texte.
J'ai déjà répondu à la même question ( réponse )
Classe:
class OutlineSpan(
@ColorInt private val strokeColor: Int,
@Dimension private val strokeWidth: Float
): ReplacementSpan() {
override fun getSize(
Paint: Paint,
text: CharSequence,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
return Paint.measureText(text.toString().substring(start until end)).toInt()
}
override fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
Paint: Paint
) {
val originTextColor = Paint.color
Paint.apply {
color = strokeColor
style = Paint.Style.STROKE
this.strokeWidth = [email protected]
}
canvas.drawText(text, start, end, x, y.toFloat(), Paint)
Paint.apply {
color = originTextColor
style = Paint.Style.FILL
}
canvas.drawText(text, start, end, x, y.toFloat(), Paint)
}
}
Bibliothèque: OutlineSpan
crédit à @YGHM ajouter un support shadow
package com.megvii.demo;
import Android.content.Context;
import Android.content.res.TypedArray;
import Android.graphics.Canvas;
import Android.graphics.Color;
import Android.graphics.Paint;
import Android.util.AttributeSet;
public class TextViewOutline extends Android.support.v7.widget.AppCompatTextView {
// constants
private static final int DEFAULT_OUTLINE_SIZE = 0;
private static final int DEFAULT_OUTLINE_COLOR = Color.TRANSPARENT;
// data
private int mOutlineSize;
private int mOutlineColor;
private int mTextColor;
private float mShadowRadius;
private float mShadowDx;
private float mShadowDy;
private int mShadowColor;
public TextViewOutline(Context context) {
this(context, null);
}
public TextViewOutline(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
private void setAttributes(AttributeSet attrs) {
// set defaults
mOutlineSize = DEFAULT_OUTLINE_SIZE;
mOutlineColor = DEFAULT_OUTLINE_COLOR;
// text color
mTextColor = getCurrentTextColor();
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TextViewOutline);
// outline size
if (a.hasValue(R.styleable.TextViewOutline_outlineSize)) {
mOutlineSize = (int) a.getDimension(R.styleable.TextViewOutline_outlineSize, DEFAULT_OUTLINE_SIZE);
}
// outline color
if (a.hasValue(R.styleable.TextViewOutline_outlineColor)) {
mOutlineColor = a.getColor(R.styleable.TextViewOutline_outlineColor, DEFAULT_OUTLINE_COLOR);
}
// shadow (the reason we take shadow from attributes is because we use API level 15 and only from 16 we have the get methods for the shadow attributes)
if (a.hasValue(R.styleable.TextViewOutline_Android_shadowRadius)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowDx)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowDy)
|| a.hasValue(R.styleable.TextViewOutline_Android_shadowColor)) {
mShadowRadius = a.getFloat(R.styleable.TextViewOutline_Android_shadowRadius, 0);
mShadowDx = a.getFloat(R.styleable.TextViewOutline_Android_shadowDx, 0);
mShadowDy = a.getFloat(R.styleable.TextViewOutline_Android_shadowDy, 0);
mShadowColor = a.getColor(R.styleable.TextViewOutline_Android_shadowColor, Color.TRANSPARENT);
}
a.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setPaintToOutline();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void setPaintToOutline() {
Paint paint = getPaint();
Paint.setStyle(Paint.Style.STROKE);
Paint.setStrokeWidth(mOutlineSize);
super.setTextColor(mOutlineColor);
super.setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
private void setPaintToRegular() {
Paint paint = getPaint();
Paint.setStyle(Paint.Style.FILL);
Paint.setStrokeWidth(0);
super.setTextColor(mTextColor);
super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
mTextColor = color;
}
public void setOutlineSize(int size) {
mOutlineSize = size;
}
public void setOutlineColor(int color) {
mOutlineColor = color;
}
@Override
protected void onDraw(Canvas canvas) {
setPaintToOutline();
super.onDraw(canvas);
setPaintToRegular();
super.onDraw(canvas);
}
}
attr définit
<declare-styleable name="TextViewOutline">
<attr name="outlineSize" format="dimension"/>
<attr name="outlineColor" format="color|reference"/>
<attr name="Android:shadowRadius"/>
<attr name="Android:shadowDx"/>
<attr name="Android:shadowDy"/>
<attr name="Android:shadowColor"/>
</declare-styleable>
code XML ci-dessous
<com.megvii.demo.TextViewOutline
Android:id="@+id/product_name"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_gravity="center_horizontal"
Android:layout_marginTop="110dp"
Android:background="#f4b222"
Android:fontFamily="@font/kidsmagazine"
Android:padding="10dp"
Android:shadowColor="#d7713200"
Android:shadowDx="0"
Android:shadowDy="8"
Android:shadowRadius="1"
Android:text="LIPSTICK SET"
Android:textColor="@Android:color/white"
Android:textSize="30sp"
app:outlineColor="#cb7800"
app:outlineSize="3dp" />
MagicTextView est très utile pour créer une police de trait, mais dans mon cas, cela provoque une erreur comme this Cette erreur est causée par des attributs d'arrière-plan de duplication définis par MagicTextView.
il faut donc éditer attrs.xml et MagicTextView.Java
attrs.xml
<attr name="background" format="reference|color" />
↓
<attr name="mBackground" format="reference|color" />
MagicTextView.Java 88:95
if (a.hasValue(R.styleable.MagicTextView_mBackground)) {
Drawable background = a.getDrawable(R.styleable.MagicTextView_mBackground);
if (background != null) {
this.setBackgroundDrawable(background);
} else {
this.setBackgroundColor(a.getColor(R.styleable.MagicTextView_mBackground, 0xff000000));
}
}