¿Cómo hago una línea punteada / discontinua en Android?

254

Estoy tratando de hacer una línea punteada. Estoy usando esto ahora para una línea sólida:

LinearLayout divider = new LinearLayout( this );
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, 2 );
divider.setLayoutParams( params );
divider.setBackgroundColor( getResources().getColor( R.color.grey ) );

Necesito algo como esto, pero punteado en lugar de sólido. Me gustaría evitar que cientos de diseños se alternen entre un diseño transparente y un diseño sólido.

Jason Robinson
fuente

Respuestas:

517

Sin código java:

drawable / dotted.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">

    <stroke
       android:color="#C7B299"
       android:dashWidth="10px"
       android:dashGap="10px"
       android:width="1dp"/>
</shape>

view.xml:

<ImageView
    android:layout_width="match_parent"
    android:layout_height="5dp"
    android:src="@drawable/dotted"
    android:layerType="software" />
embo
fuente
21
No entiendo por qué obtengo una línea sin espacios en mi teléfono, mientras veo los espacios en el Diseño gráfico en Eclipse.
Dante
73
No importa, he encontrado la solución aquí: stackoverflow.com/questions/10843402/… Simplemente configure el tipo de capa en software: android: layerType = "software"
Dante
77
Hay un error conocido cuando se usa canvas.drawLine(). Como alternativa para deshabilitar la aceleración de hardware, puede usar canvas.drawPathen su lugar.
hgoebl
10
usar en dp lugar de px .
S.M_Emamian
77
Verificó que esto funciona en Android 6.0.1 y 4.4.4. Dos cosas a tener en cuenta: 1) realmente necesita un ImageView, una vista con el dibujo como fondo no lo hizo; 2) android:layerType="software"debe establecerse en ImageView.
Jonik
206

el efecto del camino se establece en el objeto de pintura

Paint fgPaintSel = new Paint();
fgPaintSel.setARGB(255, 0, 0,0);
fgPaintSel.setStyle(Style.STROKE);
fgPaintSel.setPathEffect(new DashPathEffect(new float[] {10,20}, 0));

puede crear todo tipo de patrones de puntos al proporcionar más números en la matriz int [] que especifica las proporciones de guión y espacio. Esta es una línea simple, igualmente discontinua.

siliconaagle
fuente
¿Por qué esto hace ahora para mí? stackoverflow.com/questions/12401311/…
Chris.Zou
pruebe el nuevo flotador [] {5,10,15,20} en el constructor
siliconaagle
44
Soy un poco nuevo en el desarrollo de Android. Quiero preguntar cómo se agrega el objeto de pintura con efecto discontinuo al diseño lineal que acabo de crear.
DirkJan
Copié y pegué este código directamente, pero no pasó nada. ¿Debo escribir código adicional para que funcione?
Jason
50

Crear línea de puntos usando XML.
Cree xml en una carpeta dibujable y dele ese fondo al elemento en el que desea establecer el borde punteado.

Crear fondo XML "dashed_border":

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape>
            <solid android:color="#ffffff" />
            <stroke
                android:dashGap="5dp"
                android:dashWidth="5dp"
                android:width="1dp"
                android:color="#0000FF" />
            <padding
                android:bottom="5dp"
                android:left="5dp"
                android:right="5dp"
                android:top="5dp" />
        </shape>
    </item>
</layer-list>

Agregar ese fondo al elemento:

<Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/dashed_border"/>
Sargam
fuente
Si necesita radio para las esquinas, simplemente agregue <corners android:radius="5dp" />a su forma. Consulte stackoverflow.com/a/41940609/1000551 para obtener más información
Vadim Kotov,
Creo que la lista de capas es innecesaria para un solo elemento. Usé la <forma> como elemento raíz y funciona igual.
big_m
37

Crear xml (view_line_dotted.xml):

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:bottom="-1dp"
            android:left="-1dp"
            android:right="-1dp"
            android:top="0dp">

            <shape android:shape="rectangle">
                <stroke
                    android:width="1dp"
                    android:color="#ffff0017"
                    android:dashGap="3dp"
                    android:dashWidth="1dp" />

                <solid android:color="@android:color/transparent" />

                <padding
                    android:bottom="10dp"
                    android:left="10dp"
                    android:right="10dp"
                    android:top="10dp" />
            </shape>
        </item>
</layer-list>

Establecer como fondo de su vista:

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@drawable/view_line_dotted" />
tier777
fuente
¿Puedes explicarlo por favor?
skywall
1
Gracias, la pista es <item android:bottom="-1dp" android:left="-1dp" android:right="-1dp" >, si quieres una línea horizontal de 1dp de grosor.
CoolMind
Increíble. Casi funciona fuera de la caja para mí. El único cambio que necesitaba era cambiar la etiqueta de trazo; 2dp de ancho y 4dp dashGap.
Sufian
Para dispositivos con API <21, agréguelos android:layerType="software"a una vista o escritura view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)(consulte stackoverflow.com/questions/10843402/… ).
CoolMind
24

Lo que hice cuando quería dibujar una línea punteada es definir un dash_line.xml dibujable :

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
    android:shape="line" >
<stroke
    android:dashGap="3dp"
    android:dashWidth="2dp"
    android:width="1dp"
    android:color="@color/black" />
</shape>

Y luego, en el diseño, simplemente defina una vista con fondo como dash_line . Nota para incluir android: layerType = "software" , de lo contrario no funcionará.

<View
            android:layout_width="match_parent"
            android:layout_height="5dp"
            android:background="@drawable/dash_line"
            android:layerType="software" />
Tony Vu
fuente
¡Gracias amigo! ¡ android:layerType="software"Es lo que estaba buscando! ¡Salud!
Toochka
20

Tengo una línea de tablero personalizada que admite línea de tablero horizontal y vertical. código a continuación:

public class DashedLineView extends View
{
private float density;
private Paint paint;
private Path path;
private PathEffect effects;

public DashedLineView(Context context)
{
    super(context);
    init(context);
}

public DashedLineView(Context context, AttributeSet attrs)
{
    super(context, attrs);
    init(context);
}

public DashedLineView(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);
    init(context);
}

private void init(Context context)
{
    density = DisplayUtil.getDisplayDensity(context);
    paint = new Paint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(density * 4);
    //set your own color
    paint.setColor(context.getResources().getColor(R.color.XXX));
    path = new Path();
    //array is ON and OFF distances in px (4px line then 2px space)
    effects = new DashPathEffect(new float[] { 4, 2, 4, 2 }, 0);

}

@Override
protected void onDraw(Canvas canvas)
{
    // TODO Auto-generated method stub
    super.onDraw(canvas);
    paint.setPathEffect(effects);
    int measuredHeight = getMeasuredHeight();
    int measuredWidth = getMeasuredWidth();
    if (measuredHeight <= measuredWidth)
    {
        // horizontal
        path.moveTo(0, 0);
        path.lineTo(measuredWidth, 0);
        canvas.drawPath(path, paint);
    }
    else
    {
        // vertical
        path.moveTo(0, 0);
        path.lineTo(0, measuredHeight);
        canvas.drawPath(path, paint);
    }

}
}
ruidge
fuente
¿Es este enfoque preferible a la forma xml?
IgorGanapolsky
¡Perfecto! lo que estaba buscando
Job M
9

Al usar esta clase, puede aplicar el efecto "discontinuo y subrayado" al texto de varias líneas. Para usar DashPathEffect, debe desactivar el hardware Acelerado de su TextView (aunque el método DashPathEffect tiene un problema con el texto largo). Puede encontrar mi proyecto de muestra aquí: https://github.com/jintoga/Dashed-Underlined-TextView/blob/master/Untitled.png .

public class DashedUnderlineSpan implements LineBackgroundSpan, LineHeightSpan {

    private Paint paint;
    private TextView textView;
    private float offsetY;
    private float spacingExtra;

    public DashedUnderlineSpan(TextView textView, int color, float thickness, float dashPath,
                               float offsetY, float spacingExtra) {
        this.paint = new Paint();
        this.paint.setColor(color);
        this.paint.setStyle(Paint.Style.STROKE);
        this.paint.setPathEffect(new DashPathEffect(new float[] { dashPath, dashPath }, 0));
        this.paint.setStrokeWidth(thickness);
        this.textView = textView;
        this.offsetY = offsetY;
        this.spacingExtra = spacingExtra;
    }

    @Override
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v,
                             Paint.FontMetricsInt fm) {
        fm.ascent -= spacingExtra;
        fm.top -= spacingExtra;
        fm.descent += spacingExtra;
        fm.bottom += spacingExtra;
    }

    @Override
    public void drawBackground(Canvas canvas, Paint p, int left, int right, int top, int baseline,
                               int bottom, CharSequence text, int start, int end, int lnum) {
        int lineNum = textView.getLineCount();
        for (int i = 0; i < lineNum; i++) {
            Layout layout = textView.getLayout();
            canvas.drawLine(layout.getLineLeft(i), layout.getLineBottom(i) - spacingExtra + offsetY,
                    layout.getLineRight(i), layout.getLineBottom(i) - spacingExtra + offsetY,
                    this.paint);
        }
    }
}

Resultado:

subrayado discontinuo

Nguyen Quoc Dat
fuente
8

Si está buscando una línea vertical, use este dibujable.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:top="-8dp"
        android:bottom="-8dp"
        android:left="-8dp">
        <shape>
            <solid android:color="@android:color/transparent"/>
            <stroke
                android:width="4dp"
                android:color="#ffffff"
                android:dashGap="4dp"
                android:dashWidth="4dp"/>
        </shape>
    </item>
</layer-list>

Los valores negativos superiores, inferiores e izquierdos eliminan los lados no deseados de la forma dejando una sola línea discontinua.

Úselo en una vista como esta.

<View
android:layout_width="4dp"
android:layout_height="match_parent"
android:background="@drawable/dash_line_vertical"
android:layerType="software" />
Duncan Hoggan
fuente
4

He usado lo siguiente como fondo para el diseño:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
<stroke
   android:width="1dp"
    android:dashWidth="10px"
   android:dashGap="10px"
    android:color="android:@color/black" 
   />
</shape>
udai
fuente
¿Por qué en android:shape="rectangle"lugar de línea?
IgorGanapolsky
4

He creado una línea punteada discontinua para EditText. Aqui tienes. Crea tu nuevo xml. ej. dashed_border.xml Código aquí:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:bottom="1dp"
    android:left="-2dp"
    android:right="-2dp"
    android:top="-2dp">
    <shape android:shape="rectangle">
        <stroke
            android:width="2dp"
            android:color="#000000"
            android:dashGap="3dp"
            android:dashWidth="1dp" />

        <solid android:color="#00FFFFFF" />

        <padding
            android:bottom="10dp"
            android:left="10dp"
            android:right="10dp"
            android:top="10dp" />
    </shape>
</item></layer-list>

Y use su nuevo archivo xml en su EditText, por ejemplo:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/dashed_border"/>

¡Salud! :)

Wiktor Kalinowski
fuente
3

La mejor solución para el fondo punteado que funciona perfecto

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
  <stroke
    android:dashGap="3dp"
    android:dashWidth="2dp"
    android:width="1dp"
    android:color="@color/colorBlack" />
</shape>
Arun Prajapati
fuente
3

Para un efecto punteado en un lienzo, establezca este atributo en el objeto de pintura:

paint.setPathEffect(new DashPathEffect(new float[] {0,30}, 0));

Y cambie el valor 30 según su renderizado le convenga: representa la "distancia" entre cada punto.

ingrese la descripción de la imagen aquí

Arnaud
fuente
2

Lo único que funcionó para mí y creo que es la forma más simple es usar una ruta con un objeto de pintura como este:

    Paint paintDash = new Paint();
    paintDash.setARGB(255, 0, 0, 0);
    paintDash.setStyle(Paint.Style.STROKE);
    paintDash.setPathEffect(new DashPathEffect(new float[]{10f,10f}, 0));
    paintDash.setStrokeWidth(2);
    Path pathDashLine = new Path();

Luego onDraw (): (restablecimiento de llamada importante si cambia esos puntos entre llamadas pendientes, porque la ruta guarda todos los movimientos)

    pathDashLine.reset();
    pathDashLine.moveTo(porigenX, porigenY);
    pathDashLine.lineTo(cursorX,cursorY);
    c.drawPath(pathDashLine, paintDash);
takluiper
fuente
2

Me gustó la solución de Ruidge , pero necesitaba más control de XML. Entonces lo cambié a Kotlin y agregué atributos.

1) Copie la clase Kotlin:

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View

class DashedDividerView : View {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)

companion object {
    const val DIRECTION_VERTICAL = 0
    const val DIRECTION_HORIZONTAL = 1
}

private var dGap = 5.25f
private var dWidth = 5.25f
private var dColor = Color.parseColor("#EE0606")
private var direction = DIRECTION_HORIZONTAL
private val paint = Paint()
private val path = Path()

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
    context,
    attrs,
    defStyleAttr
) {
    val typedArray = context.obtainStyledAttributes(
        attrs,
        R.styleable.DashedDividerView,
        defStyleAttr,
        R.style.DashedDividerDefault
    )

    dGap = typedArray.getDimension(R.styleable.DashedDividerView_dividerDashGap, dGap)
    dWidth = typedArray.getDimension(R.styleable.DashedDividerView_dividerDashWidth, dWidth)
    dColor = typedArray.getColor(R.styleable.DashedDividerView_dividerDashColor, dColor)
    direction =
        typedArray.getInt(R.styleable.DashedDividerView_dividerDirection, DIRECTION_HORIZONTAL)

    paint.color = dColor
    paint.style = Paint.Style.STROKE
    paint.pathEffect = DashPathEffect(floatArrayOf(dWidth, dGap), 0f)
    paint.strokeWidth = dWidth

    typedArray.recycle()
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    path.moveTo(0f, 0f)

    if (direction == DIRECTION_HORIZONTAL) {
        path.lineTo(measuredWidth.toFloat(), 0f)
    } else {
        path.lineTo(0f, measuredHeight.toFloat())
    }
    canvas.drawPath(path, paint)
}

}

2) Cree un archivo attr en el directorio / res y agréguelo

 <declare-styleable name="DashedDividerView">
    <attr name="dividerDashGap" format="dimension" />
    <attr name="dividerDashWidth" format="dimension" />
    <attr name="dividerDashColor" format="reference|color" />
    <attr name="dividerDirection" format="enum">
        <enum name="vertical" value="0" />
        <enum name="horizontal" value="1" />
    </attr>
</declare-styleable>

3) Agregue un estilo al archivo de estilos

 <style name="DashedDividerDefault">
    <item name="dividerDashGap">2dp</item>
    <item name="dividerDashWidth">2dp</item>
    <!-- or any color -->
    <item name="dividerDashColor">#EE0606</item>
    <item name="dividerDirection">horizontal</item>
</style>

4) Ahora puedes usar el estilo predeterminado

<!-- here will be your path to the class -->
<com.your.package.app.DashedDividerView
    android:layout_width="match_parent"
    android:layout_height="2dp"
    />

o establecer atributos en XML

<com.your.package.app.DashedDividerView
    android:layout_width="match_parent"
    android:layout_height="2dp"
    app:dividerDirection="horizontal"
    app:dividerDashGap="2dp"
    app:dividerDashWidth="2dp"
    app:dividerDashColor="@color/light_gray"/>
Igor
fuente
2

Ninguna de estas respuestas funcionó para mí. La mayoría de estas respuestas le dan un borde semitransparente. Para evitar esto, debe envolver su contenedor una vez más con otro contenedor con su color preferido. Aquí hay un ejemplo:

Así es como se ve

dashed_border_layout.xml

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@color/black"
android:background="@drawable/dashed_border_out">
<LinearLayout
    android:layout_width="150dp"
    android:layout_height="50dp"
    android:padding="5dp"
    android:background="@drawable/dashed_border_in"
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is&#10;Dashed Container"
        android:textSize="16sp" />
</LinearLayout>

dashed_border_in.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
    <shape>
        <corners android:radius="10dp" />
        <solid android:color="#ffffff" />
        <stroke
            android:dashGap="5dp"
            android:dashWidth="5dp"
            android:width="3dp"
            android:color="#0000FF" />
        <padding
            android:bottom="5dp"
            android:left="5dp"
            android:right="5dp"
            android:top="5dp" />
    </shape>
</item>

dashed_border_out.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
    <shape>
        <corners android:radius="12dp" />
    </shape>
</item>

Yunus Emre
fuente
1

No sé por qué, pero las respuestas votadas no me funcionan. Lo escribo de esta manera y funciona bien.
Definir una vista personalizada:

public class XDashedLineView extends View {

private Paint   mPaint;
private Path    mPath;
private int     vWidth;
private int     vHeight;

public XDashedLineView(Context context) {
    super(context);
    init();
}

public XDashedLineView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public XDashedLineView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    mPaint = new Paint();
    mPaint.setColor(Color.parseColor("#3F577C"));
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setPathEffect(new DashPathEffect(new float[] {10,10}, 0));
    mPath = new Path();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    this.vWidth = getMeasuredWidth();
    this.vHeight = getMeasuredHeight();
    mPath.moveTo(0, this.vHeight / 2);
    mPath.quadTo(this.vWidth / 2, this.vHeight / 2, this.vWidth, this.vHeight / 2);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath, mPaint);
}
}

Entonces puedes usarlo en tu xml:

        <com.YOUR_PACKAGE_NAME.XDashedLineView
        android:layout_width="690dp"
        android:layout_height="1dp"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="620dp"/>
Kyeson
fuente
1

He creado una biblioteca con una vista personalizada para resolver este problema, y ​​debería ser muy simple de usar. Vea https://github.com/Comcast/DahDit para más información. Puede agregar líneas discontinuas como esta:

<com.xfinity.dahdit.DashedLine
    android:layout_width="250dp"
    android:layout_height="wrap_content"
    app:dashHeight="4dp"
    app:dashLength="8dp"
    app:minimumDashGap="3dp"
    app:layout_constraintRight_toRightOf="parent"
    android:id="@+id/horizontal_dashes"/>
Calvin
fuente
Gracias por compartir. Voy a tener que refinarlo para mi caso de uso, pero esto es exactamente lo que necesitaba para comenzar con vistas personalizadas. Salud.
0

Similar a tier777 aquí hay una solución para una línea horizontal:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:top="-1dp">
        <shape android:shape="line">
            <stroke
                android:width="1dp"
                android:color="#111"
                android:dashWidth="8dp"
                android:dashGap="2dp"
                />
            <solid android:color="@android:color/transparent" />
        </shape>
    </item>
</layer-list>

La idea es <item android:top="-1dp">.

Para mostrar la línea punteada en dispositivos antiguos (<= API 21), debe crear una vista con android:layerType="software"(vea el posible error de ICS potencial de la línea punteada de Android ):

<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@drawable/dashed_line"
    android:layerType="software"
    />

También se puede añadir el mismo punto de vista, sin android:layerType="software"que layout-v23para un mejor rendimiento, pero no estoy seguro de que funcionará en todos los dispositivos con API 23.

CoolMind
fuente