I am developing an Android application.
I am using "com.yuyakaido.android:card-stack-view:2.3.4" for card stack library.
The ImageView is in LinearLayout in ScrollView in CardView.
What I want to do is to fit the image in the card's size as per the picture below when the card is loaded at first.
And if you scroll down, it will display some texts as per the picture below
I use Picasso to fit the image to ImageView but the image does not show.
This is the fragment which has the 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()
}
This is the Fragment layout
<?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>
This is the CardStackAdapter, here I crop and fit the image to ImageView with 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
}
}
This is the card_view which is the layout for viewHolder of 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>
When I run the application, I get the card without the image but only texts as per the picture below
How can I display the image in the size of the card when the card is loaded?
** NOTE: The image will be retrieved from the server not local drawable. I just used the images in local drawable for testing purpose.
I am not sure why the image does not show. But if I set the height and width of layoutParams of imageView as per code below, then the image shows up.
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)
}
So, now the question is whether I can set the height and width of ImageView equal to the height and width of ScrollView
(imageView
's parent's parent) or CardView
(imageView
's parent's parent's parent) NOT LinearLayout
(parent) because LinearLayout's height should be "wrap_content".
It seems like your solution is based on image's height and width, but I need to fit the image in the CardView or ScrollView
here is my onBindViewHolder code. I just removed Picasso.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.imageView.setImageResource(R.drawable.person2)
}
Here is my CardView layout
<?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>
This is what I have got on my AVD. The image does not fill the bottom of CardView because it scales based on its height and width?
What i understand from your updated question you need to to fill the ImageView
upto CardView
Height and the textView
will be visible after scrolling. This Seems tricky to with ScrollView
because we somehow need to set height dynamically match_parent
won't work with ScrollView
.
I have made a Solution below see if this workout for you. Create a custom Image view and use inside 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)
}
}
And add the NestedScrolllView
below inside 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- It might be an ugly solution with setting height with (parent.parent as View).measuredHeight
but this is all i can thing of right now . love to see some elegant solution for this. I haven't tested it with CardStackView
.
Update
Now with RecyclerView
the problem is item drawn after the adapter is set and we need to listen for the layoutManager
callback of Item drawn since.
One way to tackle this without making it complex Set the cadStackView
height to the imageView . See the code below adding only essential code.
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
}
}
}
Now in your fragment
cardStackView.post {
initCardStackView()
}
And change the dapter creation inside initCardStackView
as follows. Also you do not have to pass Context to adapter each view has a context attached to it . Also do not use FillImageView
in this case use normal ImageView
in xml layout.
// 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())