Visualização de grade com duas colunas e imagens redimensionadas automaticamente

179

Estou tentando fazer um gridview com duas colunas. Quero dizer, duas fotos por linha, lado a lado, assim como esta imagem.

insira a descrição da imagem aqui

Mas minhas fotos têm espaços entre elas, devido ao fato de não ser do mesmo tamanho. Aqui está o que estou recebendo.

insira a descrição da imagem aqui

Como você pode ver, a primeira imagem oculta a legenda que mostra o nome e o número de telefone do contato. e as outras fotos não estão esticadas corretamente.

Aqui está o meu arquivo xml do GridView . Como você pode ver, o valor columnWidthestá definido para 200dp . Gostaria que fosse automático , para que as imagens sejam redimensionadas automaticamente para cada tamanho de tela.

<?xml version="1.0" encoding="utf-8"?>
<GridView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridViewContacts"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:numColumns="2"
    android:columnWidth="200dp"
    android:stretchMode="columnWidth"    
    android:gravity="center" />

e aqui está o arquivo xml do item, que representa cada item em si.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageViewContactIcon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />

    <LinearLayout
        android:id="@+id/linearlayoutContactName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingLeft="5dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:background="#99000000"
        android:layout_alignBottom="@+id/imageViewContactIcon">

        <TextView
            android:id="@+id/textViewContactName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:textStyle="bold"
            android:textSize="15sp"
            android:text="Lorem Ipsum" />       

        <TextView
            android:id="@+id/textViewContactNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:layout_marginLeft="5dp"
            android:focusable="true"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:textSize="10sp"
            android:text="123456789" />

    </LinearLayout>

</RelativeLayout>

Então, o que eu quero é mostrar duas imagens por linha, e as imagens são redimensionadas automaticamente, independentemente do tamanho da tela. O que estou fazendo de errado no meu layout?

Obrigado.

rogcg
fonte
1
Talvez você deva tentar encapsular o ImageView em seu próprio LinearLayout. Dessa forma, você pode configurar a tag LinearLayout exatamente como você deseja e simplesmente fazer com que o ImageView a preencha.
dani
@dani o que você quer dizer? ainda posso configurar o ImageView exatamente como eu quero. você poderia me dar um exemplo?
Rogcg
Eu acho que você precisa criar polegares a partir de imagens, que são todos iguais, ex 100x100 ou o que você precisar delas. Se você puder ver na sua imagem de exemplo, o efeito que está procurando é exibido apenas em parte da imagem. E no seu projeto você apenas exibe a imagem original, o que está errado se você precisar fazer galeria. Tente procurar por polegares em web site Android Dev :)
Vasil Valchev
@VasilValchev, mas o problema é que, mesmo que eu crie miniaturas (por exemplo, 100x100), vou precisar redimensioná-lo para diferentes tamanhos de tela. Eu já uso a scaleTypepropriedade centerCrop no ImageView. O que eu preciso alcançar é uma maneira de fazer as fotos lado a lado e redimensionar automaticamente para diferentes tamanhos de tela.
Rogcg
Você sempre pode obter o tamanho da tela no código horizontal whit e apenas dividi-lo por / 2. Se você pode ver no seu projeto que o problema está na altura, se você sabe exatamente qual é o seu peso, sempre pode fazer um retângulo perfeito. Não vejo onde está o problema?
Vasil Valchev

Respostas:

402

Aqui está um método relativamente fácil de fazer isso. Jogue um GridView em seu layout, configurando o modo de alongamento para esticar as larguras das colunas, defina o espaçamento como 0 (ou o que você quiser) e defina o número de colunas como 2:

res / layout / main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:verticalSpacing="0dp"
        android:horizontalSpacing="0dp"
        android:stretchMode="columnWidth"
        android:numColumns="2"/>

</FrameLayout>

Faça um costume ImageViewque mantenha sua proporção:

src / com / exemplo / graphicstest / SquareImageView.java

public class SquareImageView extends ImageView {
    public SquareImageView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
    }
}

Faça um layout para um item de grade usando este SquareImageView e defina o scaleType como centerCrop:

res / layout / grid_item.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <com.example.graphicstest.SquareImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:layout_gravity="bottom"
        android:textColor="@android:color/white"
        android:background="#55000000"/>

</FrameLayout>

Agora faça algum tipo de adaptador para o seu GridView:

src / com / exemplo / graphicstest / MyAdapter.java

private final class MyAdapter extends BaseAdapter {
    private final List<Item> mItems = new ArrayList<Item>();
    private final LayoutInflater mInflater;

    public MyAdapter(Context context) {
        mInflater = LayoutInflater.from(context);

        mItems.add(new Item("Red",       R.drawable.red));
        mItems.add(new Item("Magenta",   R.drawable.magenta));
        mItems.add(new Item("Dark Gray", R.drawable.dark_gray));
        mItems.add(new Item("Gray",      R.drawable.gray));
        mItems.add(new Item("Green",     R.drawable.green));
        mItems.add(new Item("Cyan",      R.drawable.cyan));
    }

    @Override
    public int getCount() {
        return mItems.size();
    }

    @Override
    public Item getItem(int i) {
        return mItems.get(i);
    }

    @Override
    public long getItemId(int i) {
        return mItems.get(i).drawableId;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View v = view;
        ImageView picture;
        TextView name;

        if (v == null) {
            v = mInflater.inflate(R.layout.grid_item, viewGroup, false);
            v.setTag(R.id.picture, v.findViewById(R.id.picture));
            v.setTag(R.id.text, v.findViewById(R.id.text));
        }

        picture = (ImageView) v.getTag(R.id.picture);
        name = (TextView) v.getTag(R.id.text);

        Item item = getItem(i);

        picture.setImageResource(item.drawableId);
        name.setText(item.name);

        return v;
    }

    private static class Item {
        public final String name;
        public final int drawableId;

        Item(String name, int drawableId) {
            this.name = name;
            this.drawableId = drawableId;
        }
    }
}

Defina esse adaptador para o seu GridView:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    GridView gridView = (GridView)findViewById(R.id.gridview);
    gridView.setAdapter(new MyAdapter(this));
}

E aproveite os resultados:

Exemplo GridView

Kevin Coppock
fonte
3
funcionou! mas gostaria que você me explicasse a necessidade de criar uma classe para ImageViewe por que usar algo assim, em <com.example.graphicstest.SquareImageView>vez de uma simples <ImageView>tag no arquivo xml . Você poderia me explicar mais sobre essa relação de aspecto e como a criação dessa classe não afeta os itens. existe uma maneira de otimizar isso, sem a necessidade da ImageViewclasse personalizada ? obrigado pela sua ajuda de qualquer maneira.
Rogcg
10
Basicamente, na classe ImageView do Android, não há como simplesmente especificar "ei, mantenha uma proporção quadrada (largura / altura) para essa visualização", a menos que você codifique largura e altura. Você poderia fazer alguns ajustes manuais do LayoutParams no getView do adaptador, mas, francamente, é muito mais simples deixar o ImageView lidar com todas as medições e substituir os resultados para dizer "Qualquer que seja a largura, faça com que minha altura permaneça a mesma". Você nunca precisa pensar sobre isso, é sempre quadrado e funciona como esperado. Basicamente, esta é a maneira mais fácil de manter a vista quadrada.
Kevin Coppock
8
Não consigo pensar em um motivo para evitar fazer a classe ImageView personalizada, pois você pode fazer tudo o que puder com um ImageView normal, mas isso evita que você precise fazer medições e instanciação e verificação de LayoutParams em todos os lugares .
Kevin Coppock
1
Obrigado pela edição, pensei que poderia ser isso, trabalhando como um encanto!
Zander
1
@kcoppock: Muito obrigado por compartilhar este código. O que eu mais gosto é que você pode substituir as cores da visualização de imagens por imagens "reais" e as são redimensionadas corretamente! O que eu gostaria de entender é se é possível redimensionar as visualizações para que um certo número de visualizações preencha a tela inteira.
precisa saber é o seguinte
14

outra abordagem simples com itens internos modernos, como o PercentRelativeLayout, agora está disponível para novos usuários que enfrentam esse problema. graças à equipe android para liberar este item.

<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
app:layout_widthPercent="50%">

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#55000000"
        android:paddingBottom="15dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:textColor="@android:color/white" />

</FrameLayout>

e para obter um melhor desempenho, você pode usar algumas coisas, como o carregador de imagens picasso, que ajudam a preencher toda a largura de todos os pais de imagens. por exemplo, no seu adaptador, você deve usar isto:

int width= context.getResources().getDisplayMetrics().widthPixels;
    com.squareup.picasso.Picasso
            .with(context)
            .load("some url")
            .centerCrop().resize(width/2,width/2)
            .error(R.drawable.placeholder)
            .placeholder(R.drawable.placeholder)
            .into(item.drawableId);

agora você não precisa mais da classe CustomImageView.

PS: eu recomendo usar o ImageView no lugar do Type Int na classe Item.

espero que isso ajude ..

Setmax
fonte
Com a API 26.1, isso foi privado. Veja o link
Dominikus K.