Como crio um ListView com cantos arredondados no Android?

176

Como crio um ListView com cantos arredondados no Android?

lenda
fonte
Esta pergunta é realmente útil, assim como sua resposta .. !!
Sajad Karuthedath

Respostas:

371

Aqui está uma maneira de fazê-lo (graças à documentação do Android!):

Adicione o seguinte em um arquivo (por exemplo, costumeshape.xml) e, em seguida, coloque-o em (res / drawable / costumeshape.xml)

<?xml version="1.0" encoding="UTF-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
     android:shape="rectangle"> 
     <gradient 
         android:startColor="#SomeGradientBeginColor"
         android:endColor="#SomeGradientEndColor" 
         android:angle="270"/> 

    <corners 
         android:bottomRightRadius="7dp" 
         android:bottomLeftRadius="7dp" 
         android:topLeftRadius="7dp" 
         android:topRightRadius="7dp"/> 
</shape> 

Depois de criar esse arquivo, basta definir o plano de fundo de uma das seguintes maneiras:

Através do código: listView.setBackgroundResource(R.drawable.customshape);

Por meio do XML , basta adicionar o seguinte atributo ao contêiner (ex: LinearLayout ou a qualquer campo):

android:background="@drawable/customshape"

Espero que alguém ache útil ...

lenda
fonte
2
Obrigado pela ótima dica. Apenas para sua informação, copiar e colar me deu uma exceção de tempo de execução dizendo: "XmlPullParserException: a tag <gradient> da linha de arquivo XML binária nº 4 requer que o atributo 'angle' seja um múltiplo de 45". Facilmente remediado alterando o ângulo para 270.
25/01
Obrigado pela correção ... Mas não sei por que isso poderia estar acontecendo. Você encontrou algum motivo específico?
Legenda
@teedyay: Anytime pal :)
Legend
1
igual a todas as garras, o ângulo deve ser múltiplo de 45: "XmlPullParserException: a tag <gradient> da linha de arquivo XML binária # 4 requer que o atributo 'angle' seja um múltiplo de 45"
Youssef
29
Porém, ele não funciona bem com o destaque da seleção: quando o item superior ou inferior é selecionado, o plano de fundo colorido é retangular e desenhado na parte superior do plano de fundo com cantos arredondados.
Kris Van Bael
56

Embora isso tenha funcionado, também removeu toda a cor de fundo. Eu estava procurando uma maneira de fazer apenas a borda e apenas substituir esse código de layout XML por este e estava pronto!

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="4dp" android:color="#FF00FF00" />
    <padding android:left="7dp" android:top="7dp"
            android:right="7dp" android:bottom="7dp" />
    <corners android:radius="4dp" />
</shape> 
Kevin Parker
fonte
Verifique também esta resposta
ThomasRS 21/03
12

@ kris-van-bael

Para aqueles que têm problemas com a seleção, destaque para a linha superior e inferior, onde o retângulo de fundo aparece na seleção, você precisa definir o seletor para a sua exibição de lista em cores transparentes.

listView.setSelector(R.color.transparent);

Em color.xml, adicione o seguinte -

<color name="transparent">#00000000</color>
alvins
fonte
5
não funcionou para mim. Eu adicionei a seguinte linha, no entanto, e ela se livrou dela:android:cacheColorHint="@android:color/transparent"
cesar
1
Definitivamente, isso corrigiu o problema de seleção para mim - obrigado! Você também pode usar android.R.color.transparent para a cor Selector em vez de criar a sua própria.
Greg7gkb
3
Em vez de fazê-lo através de programação, adicionar este à sua ListView no layout XML para esconder seleção de cores: android: listSelector = "# 00000000"
Elad Nava
@alvins Existe alguma maneira de tornar o Layout selecionável para destacar como o item de exibição de lista?
Bharat Dodeja
o que devo fazer se quiser usar uma cor opaca para listSelector?
suitianshi
3

As outras respostas são muito úteis, graças aos autores!

Mas não consegui ver como personalizar o retângulo ao destacar um item na seleção, em vez de desabilitar o realce @alvins @bharat dojeha.

A seguir, trabalha para mim criar um contêiner de item de exibição de lista arredondado sem contorno e um cinza mais claro quando selecionado da mesma forma:

Seu xml precisa conter um seletor como, por exemplo, (em res / drawable / costumeshape.xml):

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
    <shape xmlns:android="http://schemas.android.com/apk/res/android" >
        <stroke android:width="8dp" android:color="@android:color/transparent" />
        <padding android:left="14dp" android:top="14dp"
                android:right="14dp" android:bottom="14dp" />
        <corners android:radius="10dp" />
        <gradient 
             android:startColor="@android:color/background_light"
             android:endColor="@android:color/transparent" 
             android:angle="225"/> 
    </shape>
</item>
<item android:state_pressed="false">
    <shape xmlns:android="http://schemas.android.com/apk/res/android" >
        <stroke android:width="8dp" android:color="@android:color/transparent" />
        <padding android:left="14dp" android:top="14dp"
                android:right="14dp" android:bottom="14dp" />
        <corners android:radius="10dp" />
        <gradient 
             android:startColor="@android:color/darker_gray"
             android:endColor="@android:color/transparent" 
             android:angle="225"/> 
    </shape>        
</item>

Em seguida, você precisa implementar um adaptador de lista e substituir o método getView para definir o seletor personalizado como plano de fundo

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //snip
        convertView.setBackgroundResource(R.drawable.customshape);
        //snip
    }

e também preciso "ocultar" o retângulo do seletor padrão, por exemplo, no onCreate (também oculto minha fina linha divisória cinza entre os itens):

listView.setSelector(android.R.color.transparent);
listview.setDivider(null);

Essa abordagem resolve uma solução geral para drawables, não apenas o ListViewItem com vários estados de seleção.

dr_g
fonte
3

Atualizar

A solução hoje em dia é usar um CardViewcom suporte para cantos arredondados incorporados.


Resposta original *

Outra maneira que encontrei foi mascarar seu layout desenhando uma imagem por cima do layout. Isso pode ajudá-lo. Confira os cantos cortados arredondados do XML do Android

Richard Le Mesurier
fonte
2

Ainda outra solução para a seleção destaca problemas com o primeiro e o último itens da lista:

Adicione preenchimento na parte superior e inferior do fundo da lista igual ou superior ao raio. Isso garante que o destaque da seleção não se sobreponha às suas curvas de canto.

Essa é a solução mais fácil quando você precisa de um destaque de seleção não transparente.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="@color/listbg" />
    <stroke
        android:width="2dip"
        android:color="#D5D5D5" />
    <corners android:radius="10dip" />

    <!-- Make sure bottom and top padding match corner radius -->
    <padding
        android:bottom="10dip"
        android:left="2dip"
        android:right="2dip"
        android:top="10dip" />
</shape>
William Morrison
fonte
1

Isso foi incrivelmente útil para mim. Gostaria de sugerir outra solução alternativa para destacar perfeitamente os cantos arredondados, se você estiver usando seu próprioCustomAdapter .

Definindo arquivos XML

Primeiro de tudo, vá para dentro da sua pasta de desenho e crie 4 formas diferentes:

  • shape_top

    <gradient
        android:startColor="#ffffff"
        android:endColor="#ffffff"
        android:angle="270"/>
    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"/>

  • shape_normal

    <gradient
        android:startColor="#ffffff"
        android:endColor="#ffffff"
        android:angle="270"/>
    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"/>

  • shape_bottom

    <gradient
        android:startColor="#ffffff"
        android:endColor="#ffffff"
        android:angle="270"/>
    <corners
        android:bottomRightRadius="10dp"
        android:bottomRightRadius="10dp"/>

  • shape_rounded

    <gradient
        android:startColor="#ffffff"
        android:endColor="#ffffff"
        android:angle="270"/>
    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"
        android:bottomRightRadius="10dp"
        android:bottomRightRadius="10dp"/>

Agora, crie um layout de linha diferente para cada forma, ou seja, para shape_top:

  • Você também pode fazer isso programaticamente alterando o plano de fundo.

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="10dp"
        android:fontFamily="sans-serif-light"
        android:text="TextView"
        android:textSize="22dp" />
    
    <TextView
        android:id="@+id/txtValue1"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:textSize="22dp"
        android:layout_gravity="right|center"
        android:gravity="center|right"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="35dp"
        android:text="Fix"
        android:scaleType="fitEnd" />

E defina um seletor para cada lista em forma, ou seja, para shape_top:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Selected Item -->

    <item android:state_selected="true"
        android:drawable="@drawable/shape_top" />
    <item android:state_activated="true"
        android:drawable="@drawable/shape_top" />

    <!-- Default Item -->
    <item android:state_selected="false"
        android:drawable="@android:color/transparent" />
</selector>

Mude seu CustomAdapter

Por fim, defina as opções de layout dentro de CustomAdapter:

if(position==0)
{
 convertView = mInflater.inflate(R.layout.list_layout_top, null);
}
else
{
 convertView = mInflater.inflate(R.layout.list_layout_normal, null);
}

if(position==getCount()-1)
{
convertView = mInflater.inflate(R.layout.list_layout_bottom, null);
}

if(getCount()==1)
{
convertView = mInflater.inflate(R.layout.list_layout_unique, null);
}

E pronto!

Machado
fonte
1

fazer fronteira u tem que fazer outro arquivo xml com propriedade solid e cantos na pasta drawable e chama em background

pawan kumar
fonte
0

Estou usando uma exibição personalizada que eu layout em cima dos outros e que apenas desenha os 4 cantos pequenos na mesma cor que o plano de fundo. Isso funciona independentemente do conteúdo da visualização e não aloca muita memória.

public class RoundedCornersView extends View {
    private float mRadius;
    private int mColor = Color.WHITE;
    private Paint mPaint;
    private Path mPath;

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

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

        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.RoundedCornersView,
                0, 0);

        try {
            setRadius(a.getDimension(R.styleable.RoundedCornersView_radius, 0));
            setColor(a.getColor(R.styleable.RoundedCornersView_cornersColor, Color.WHITE));
        } finally {
            a.recycle();
        }
    }

    private void init() {
        setColor(mColor);
        setRadius(mRadius);
    }

    private void setColor(int color) {
        mColor = color;
        mPaint = new Paint();
        mPaint.setColor(mColor);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);

        invalidate();
    }

    private void setRadius(float radius) {
        mRadius = radius;
        RectF r = new RectF(0, 0, 2 * mRadius, 2 * mRadius);
        mPath = new Path();
        mPath.moveTo(0,0);
        mPath.lineTo(0, mRadius);
        mPath.arcTo(r, 180, 90);
        mPath.lineTo(0,0);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        /*Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawRect(0, 0, mRadius, mRadius, paint);*/

        int w = getWidth();
        int h = getHeight();
        canvas.drawPath(mPath, mPaint);
        canvas.save();
        canvas.translate(w, 0);
        canvas.rotate(90);
        canvas.drawPath(mPath, mPaint);
        canvas.restore();
        canvas.save();
        canvas.translate(w, h);
        canvas.rotate(180);
        canvas.drawPath(mPath, mPaint);
        canvas.restore();
        canvas.translate(0, h);
        canvas.rotate(270);
        canvas.drawPath(mPath, mPaint);
    }
}
mbonnin
fonte