ImageView em LinearLayout em Scrollview com CardStackView e a imagem de Picasso não mostra

Sep 09 2020

Estou desenvolvendo um aplicativo Android.

Estou usando "com.yuyakaido.android:card-stack-view:2.3.4" para a biblioteca de pilha de cartões.

O ImageView está em LinearLayout em ScrollView em CardView.

O que eu quero fazer é ajustar a imagem no tamanho do cartão conforme a imagem abaixo quando o cartão é colocado pela primeira vez.

E se você rolar para baixo, ele irá mostrar alguns textos conforme a imagem abaixo

Eu uso o Picasso para ajustar a imagem ao ImageView, mas a imagem não aparece.

Este é o fragmento que possui o CardStackView

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.fragment_card_stack, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initCardStackView()
}

private fun initCardStackView() {
    manager = CardStackLayoutManager(context)
    manager.setCanScrollVertical(false)
    manager.setSwipeableMethod(SwipeableMethod.Manual)
    adapter = CardStackAdapter(context, createDummyProfiles())
    cardStackView.layoutManager = manager
    cardStackView.adapter = adapter
    cardStackView.itemAnimator = DefaultItemAnimator()
}

Este é o layout Fragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <com.yuyakaido.android.cardstackview.CardStackView
        android:id="@+id/cardStackView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="80dp"
        android:clipToPadding="false"/>

</LinearLayout>

Este é o CardStackAdapter, aqui eu recortei e ajustei a imagem para ImageView com Piccaso

class CardStackAdapter(private var context: Context?, private var movies: MutableList<Movies>) :
    RecyclerView.Adapter<CardStackAdapter.ViewHolder>() {

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return ViewHolder(inflater.inflate(R.layout.card_view, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        Picasso.get().load(R.drawable.person3).fit().centerCrop().into(holder.itemView.imageView)
    }

    override fun getItemCount(): Int {
        return movies.size
    }

    fun setItems(items: MutableList<Profile>) {
        this.movies = items
    }
}

Este é card_view, que é o layout para viewHolder de CardStackAdapter

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?attr/selectableItemBackground"
    android:foreground="?attr/selectableItemBackground"
    app:cardBackgroundColor="@android:color/white"
    app:cardCornerRadius="18dp"
    app:cardPreventCornerOverlap="false"
    app:cardUseCompatPadding="true">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/imageView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/lipsum"/>

        </LinearLayout>

    </ScrollView>

</androidx.cardview.widget.CardView>

Quando executo o aplicativo, recebo o cartão sem a imagem, mas apenas os textos conforme a imagem abaixo

Como posso exibir a imagem no tamanho do cartão quando o cartão está carregado?



------------------------- ATUALIZAÇÃO ------------------------ ----

** NOTA: A imagem será recuperada do servidor não drawable local. Acabei de usar as imagens no drawable local para fins de teste.

Não sei por que a imagem não aparece. Mas se eu definir a altura e a largura de layoutParams de imageView de acordo com o código abaixo, a imagem aparecerá.

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.itemView.imageView.layoutParams.height = 1918
    holder.itemView.imageView.layoutParams.width = 970
    Picasso.get().load(R.drawable.person3).fit().centerCrop().into(holder.itemView.imageView)
}

Então, agora a questão é se eu posso definir a altura e largura de ImageView igual à altura e largura de ScrollView( imageViewpai do pai de) ou CardView( imageViewpai do pai de) NÃO LinearLayout (pai) porque a altura do LinearLayout deve ser "wrap_content".



------------------------- ATUALIZAÇÃO ------------------------ ----

Parece que a sua solução é baseada na altura e largura da imagem, mas eu preciso ajustar a imagem no CardView ou ScrollView

aqui está meu código onBindViewHolder. Acabei de remover o Picasso.

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.itemView.imageView.setImageResource(R.drawable.person2)
}

Aqui está o meu layout CardView

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?attr/selectableItemBackground"
    android:foreground="?attr/selectableItemBackground"
    app:cardCornerRadius="18dp"
    app:cardPreventCornerOverlap="false"
    app:cardUseCompatPadding="true">

    <androidx.core.widget.NestedScrollView
        android:fillViewport="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/imageView"
                android:src="@drawable/person2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:scaleType="centerCrop"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/lipsum"/>

        </LinearLayout>

    </androidx.core.widget.NestedScrollView>

</androidx.cardview.widget.CardView>

Isso é o que tenho no meu AVD. A imagem não preenche a parte inferior do CardView porque é dimensionada com base em sua altura e largura?

Respostas

1 ADM Sep 17 2020 at 19:08

O que eu entendi da sua pergunta atualizada você precisa preencher ImageViewaté CardViewHeight e textViewficará visível após rolar. Parece complicado ScrollViewporque, de alguma forma, precisamos definir a altura dinamicamente com o match_parentqual não funcionará ScrollView.
Eu fiz uma Solução abaixo para ver se esse treino é para você. Crie uma visualização de imagem personalizada e use-a dentro CardView.

class FillImageView : AppCompatImageView {
constructor(context: Context?) : super(context!!) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context!!, attrs, defStyleAttr) {}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    val parentWidth = MeasureSpec.getSize(widthMeasureSpec)
    setMeasuredDimension(parentWidth, (parent.parent as View).measuredHeight)
}
}

E adicione o NestedScrolllViewseguinte dentro do CardView

<androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:fillViewport="true"
            android:layout_height="match_parent">
        <LinearLayout
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:layout_height="wrap_content">
            <com.ace.myapplication.FillImageView
                    android:layout_width="match_parent"
                    android:src="@drawable/hello"
                    android:adjustViewBounds="true"
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"/>
            <TextView
                    android:layout_width="match_parent"
                    android:textSize="23sp"
                    android:padding="10dp"
                    android:layout_height="wrap_content"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

PS - Pode ser uma solução feia com configuração de altura, (parent.parent as View).measuredHeightmas isso é tudo que posso pensar agora. adoraria ver alguma solução elegante para isso. Eu não testei com CardStackView.

Atualizar agora com RecyclerViewo problema é o item desenhado após o adaptador ser configurado e precisamos ouvir o layoutManagerretorno de chamada do item desenhado desde então. Uma maneira de lidar com isso sem torná-lo complexo Defina a cadStackViewaltura de imageView. Veja o código abaixo adicionando apenas o código essencial.

class CardStackAdapter(val stackViewHeight:Int, private var movies: MutableList<Moview>) :
    RecyclerView.Adapter<CardStackAdapter.ViewHolder>() {

   inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val imageView:ImageView = itemView.findViewById(R.id.imageView)
        init {
            imageView.layoutParams.height= stackViewHeight
        }
    }
}

Agora em seu fragmento

cardStackView.post {
       initCardStackView()
   }

E mude a criação dapter dentro da initCardStackViewseguinte maneira. Além disso, você não precisa passar o Contexto para o adaptador; cada visualização possui um contexto anexado a ele. Também não use FillImageViewneste caso use normal ImageViewno layout xml.

// Since you are using android:padding it will be same for all sides
    val cardHeight=cardStackView.height- (2*cardStackView.paddingTop)
    val adapter = CardStackAdapter(cardHeight, createDummyProfiles())
BrunoVieira Sep 16 2020 at 02:46

Por que você está usando o Picasso para carregar um drawable local? Você pode apenas aplicar seu drawable de origem ao ImageView.

<ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:scaleType="fitCenter"
    android:src="@drawable/person3"
    android:layout_height="wrap_content" />

Além disso, recomendo fortemente que você use em NestedScrollViewvez de ScrollViewe, em seguida, defina fillViewPort(atributo do ScrollView) como true para estender sua altura igual à altura da criança


Atualizar:

Se você deseja realmente usar o Picasso para carregar um URL externo, faça a seguinte configuração:

Picasso
    .get()
    .load([url_you_want_to_load])
    .placeholder([placeholder_you_want_to_pre_load])
    .into(imageView)

e, em seguida, configure seu ImageView para caber no centro e preservar sua proporção:

<ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="centerCrop" />