Custom spacing between recyclerview items - android

I need to display items similar to a hamburger.
Each element has a different height, but the distance between them should be always the same. I tried to use LinearLayoutManager, but spaces between items were too big. I created custom LinearLayoutManager but it doesn't work as I expected. How I should make spacing like 16dp or 50 px between ImageView.
I also need to make no spacing for the same type of items, but this problem is less important.
class CustomLinearLayoutManager(context: Context) : LinearLayoutManager(context) {
private val horizontalSpace: Int
get() = width - paddingEnd - paddingStart
private val verticalSpace: Int
get() = height - paddingBottom - paddingTop
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
return spanLayoutSize(super.generateDefaultLayoutParams())
}
override fun generateLayoutParams(c: Context, attrs: AttributeSet): RecyclerView.LayoutParams {
return spanLayoutSize(super.generateLayoutParams(c, attrs))
}
override fun generateLayoutParams(lp: ViewGroup.LayoutParams): RecyclerView.LayoutParams {
return spanLayoutSize(super.generateLayoutParams(lp))
}
private fun spanLayoutSize(layoutParams: RecyclerView.LayoutParams): RecyclerView.LayoutParams {
if (orientation == HORIZONTAL) {
layoutParams.width = (horizontalSpace / itemCount)
} else if (orientation == VERTICAL) {
layoutParams.height = (verticalSpace / itemCount)
}
return layoutParams
}
override fun canScrollVertically(): Boolean {
return false
}
override fun canScrollHorizontally(): Boolean {
return false
}
}
RecyclerView_item.xml
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/RecyclerFood_item"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/ingredientPic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:contentDescription="#string/display_image_of_ingredient"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment_layout.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/DishFragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="my.custom.food-app.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/DishList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Related

Android RecyclerView horizontal GridLayoutManager with decoration bug?

guys
When I use horizontal RecyclerView ItemDecoration I encounter some problems, I want to achieve different decoration height but horizontal recyclerview decoration height is always the same, pictures and code are below, I used the same code in vertical GridLayoutManger is effective, I saw the source code should be measured when the horizontal recyclerview has special processing, this is normal?
pic
class MainItemDecoration : RecyclerView.ItemDecoration() {
private val space20 = (Resources.getSystem().displayMetrics.density * 20).toInt()
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
) {
val lp = view.layoutParams as GridLayoutManager.LayoutParams
outRect.left = space20
outRect.right = space20
when (lp.spanIndex) {
0 -> {
outRect.bottom = 0
}
1 -> {
outRect.bottom = space20
}
2 -> {
outRect.bottom = space20 * 2
}
}
}
}
----- MainActivity----
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.apply {
layoutManager = GridLayoutManager(context, 3, GridLayoutManager.HORIZONTAL, false)
adapter = MainAdapter()
addItemDecoration(MainItemDecoration())
}
}
}
--- xml-------
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_red_light"
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:listitem="#layout/item_main"
tools:orientation="horizontal"
tools:spanCount="3" />
</FrameLayout>
--- item ----
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:background="#android:color/holo_blue_light"
android:gravity="center"
android:textColor="#android:color/white"
android:textSize="16sp"
tools:text="123" />
</FrameLayout>

How to center horizontally items in recyclerview that uses gridlayoutmanager

I'm preparing a little game in my app and would like to create a recylerview based on gridlayoutmanager and my problem is that I can't center horizontally all elements inside this view.
I have already tried to fix it by some changes in layout file but it didn't help.
Adapter :
class AdapterCheckYrslf(private val word: String) :
RecyclerView.Adapter<AdapterCheckYrslf.CardViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_checkrslf_letter, parent, false)
return CardViewHolder(view)
}
override fun getItemCount(): Int {
return word.length
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.currentLetter.text = word[position].toUpperCase().toString()
}
class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val currentLetter = itemView.findViewById<TextView>(R.id.item_checkyrslf_letter)
}
}
GridSpacingItemDecoration, it makes a space beetwen every item :
class GridSpacingItemDecoration(private val space: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View,
parent: RecyclerView, state: RecyclerView.State) {
outRect.left = space
outRect.right = space
outRect.bottom = space
outRect.top = space
}
}
ColumnQty :
class ColumnQty(context: Context, viewId: Int) {
private val width: Int
private val height: Int
private var remaining: Int = 0
private val displayMetrics: DisplayMetrics
init {
val view = View.inflate(context, viewId, null)
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
width = view.measuredWidth
height = view.measuredHeight
displayMetrics = context.resources.displayMetrics
}
fun calculateNoOfColumns(): Int {
var numberOfColumns = displayMetrics.widthPixels / width
remaining = displayMetrics.widthPixels - numberOfColumns * width
if (remaining / (2 * numberOfColumns) < 15) {
numberOfColumns--
remaining = displayMetrics.widthPixels - numberOfColumns * width
}
return numberOfColumns
}
fun calculateSpacing(): Int {
val numberOfColumns = calculateNoOfColumns()
return remaining / (2 * numberOfColumns)
}
}
Activity :
val gridManagerFix = ColumnQty(this, R.layout.item_checkrslf_letter)
check_yrslf_word.layoutManager = GridLayoutManager(this, gridManagerFix.calculateNoOfColumns())
check_yrslf_word.addItemDecoration(GridSpacingItemDecoration(gridManagerFix.calculateSpacing()))
check_yrslf_word.setHasFixedSize(true)
Fragment of layout :
<LinearLayout
android:id="#+id/helper_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="#+id/check_yrslf_end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/check_yrslf_hand">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/check_yrslf_word"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:clipChildren="true"
android:clipToPadding="true"
android:numColumns="auto_fit"
android:overScrollMode="never"
android:scrollbars="none"
android:scrollingCache="true"
android:stretchMode="columnWidth"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
Item layout :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
android:id="#+id/item_checkyrslf_cardLetter"
style="#style/Widget.MaterialComponents.CardView"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_marginBottom="5dp"
android:checkable="false"
app:cardBackgroundColor="#color/grey_3"
app:cardElevation="0dp"
app:checkedIconTint="#color/white"
app:strokeColor="#color/colorAccent"
app:strokeWidth="1dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="#+id/item_checkyrslf_letter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="#font/poppins_medium"
android:gravity="center"
android:includeFontPadding="false"
android:text="A"
android:textAppearance="#style/TextAppearance.MaterialComponents.Headline1"
android:textColor="#color/textColor"
android:textSize="16sp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
Pictures :
How does this layout look in Preview
That's what I have
I would like to have
Try making the RecyclerView android:layout_width="match_parent" and android:gravity="center"
Edit 1:
Try removing all those added params to your RecyclerView: (Something like this)
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/check_yrslf_word"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Now try it, just for debugging purposes.
I have solved it by changing GridLayoutManager to LinearLayoutManager.

Resizing an image - flicker issue in collapsingtoolbarlayout

I want to have an image in collapsing toolbar layout, so when I scroll the list below, image becomes twice as small, but doesn't disappear completely.
So far I achieved this with following code:
XML:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent"
android:background="#fff"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent"
android:id="#+id/appBar"
android:elevation="1dp"
android:layout_height="300dp">
<com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent"
app:expandedTitleGravity="bottom"
android:minHeight="200dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_collapseMode="pin"
android:layout_gravity="bottom"
android:orientation="vertical">
<ImageView android:layout_width="200dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_height="200dp"
android:id="#+id/image"
android:scaleType="centerCrop"
android:src="#drawable/saya_no_uta"/>
<TextView android:id="#+id/doStuff" android:layout_width="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textColor="#fff"
android:layout_height="wrap_content" android:text="Do stuff"/>
<LinearLayout
android:id="#+id/content"
android:background="#00f"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="#id/image"
android:layout_width="match_parent"
android:layout_height="100dp">
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView android:layout_width="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:layout_height="match_parent">
<TextView android:layout_width="match_parent"
android:text="#string/large_text"
android:layout_height="wrap_content"/>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Kotlin:
class CollapsingToolbarFragment : Fragment() {
companion object {
const val TAG = "CollapsingToolbarLayout"
}
var originalHeight: Float = 0.0f
var lastVerticalOffset: Int = Int.MAX_VALUE
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = FragmentCollapsingToolbarBinding.inflate(inflater, container, false)
originalHeight = convertDpToPixel(200.0f)
binding.lifecycleOwner = viewLifecycleOwner
binding.appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
if (lastVerticalOffset == verticalOffset) {
return#OnOffsetChangedListener
}
lastVerticalOffset = verticalOffset
val totalScrollRange = appBarLayout.totalScrollRange
Log.i("Hello", "total: $totalScrollRange, offset: $verticalOffset")
val size = originalHeight.toInt() + verticalOffset
binding.image.layoutParams.height = size
binding.image.layoutParams.width = size
binding.image.requestLayout()
})
return binding.root
}
fun convertDpToPixel(dp: Float): Float {
return dp * (requireContext().resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
}
}
It works like it's supposed to, but there are side effects. When I quickly scroll up or down, for a split second I can see my textview with text "Do stuff" go above or below its place by a small margin, which is really annoying. This might be due to the fact that I'm just manually calling requestLayout() But what other ways do I have to resize image in OnOffsetChangedListener?
Or maybe I could take some other approach?
In case someone was wondering, I was able to get better results by using scaleX, scaleY and translationY properties on ImageView, instead of requesting layout. I also moved my edit TextView outside of constraint layout.
This is how my onOffsetChanged looks like on a different code base:
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
if (scrollRange == -1) {
scrollRange = binding.appBarLayout.totalScrollRange
}
val delta = 1.0f - abs(verticalOffset).toFloat() / scrollRange.toFloat() * 0.5f
binding.avatar.scaleX = 0.2f + delta * 0.8f
binding.avatar.scaleY = 0.2f + delta * 0.8f
binding.avatar.translationY = delta * 0.5f * abs(verticalOffset)
}
You can write your custom collapsingtoolbarlayout to rewrite OffsetUpdateListener to change the image size.

How to show partial item in Horizontal RecyclerView at last to indicate scrolling?

I have tried to search the answer like in this thread: RecyclerView LinearLayoutManager set item count
But it doesn't exactly solve my problem because it shows fixed items.
I want to make RecyclerView like the image below
as you can see in the picture, there are 3 items showing, but also it has incomplete item showing in the right side to give indication that this scroll view can be scrolled. I want to have that partial(3 + 1/4) item in the recyclerView for all screen size.
The xml of item layout I use is like this:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="150dp"
android:layout_height="250dp"
app:cardCornerRadius="10dp"
android:layout_marginRight="8dp"
app:cardElevation="3dp">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/background_light">
<ImageView
android:layout_width="0dp"
android:layout_height="100dp"
app:srcCompat="#drawable/logo_apps"
android:id="#+id/productImageView_Item"
android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintDimensionRatio="w,1:1"/>
<TextView
android:text="#string/product_name"
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/productName_textView_item"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/productImageView_Item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="4dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="4dp"
android:textAlignment="center"
android:minLines="1"
android:maxLines="2"
app:autoSizeMinTextSize="8sp"
app:autoSizeMaxTextSize="14sp"
android:layout_marginBottom="8dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
and the recycler view xml in the activity is like this:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragment.HomeFragment" android:background="#color/colorLightGrayBackground">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintVertical_bias="1.0"
android:id="#+id/recyclerView_1"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
and here is the code in the java file:
val layoutManager = LinearLayoutManager(mContext,LinearLayoutManager.HORIZONTAL,false)
recyclerView1.adapter = productAdapter
recyclerView1.layoutManager = layoutManager
here is my adapter:
class ProductListAdapter(val context: Context, val products: List<Product>) : RecyclerView.Adapter<ProductListAdapter.ViewHolderProductList>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderProductList {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.item_product_list,parent, false)
return ViewHolderProductList(cellForRow)
}
override fun getItemCount(): Int {
return products.size
}
override fun onBindViewHolder(holder: ViewHolderProductList, position: Int) {
val product = products[position]
holder.productNameTextView.text = product.name
Glide
.with(context)
.load(product.getFormattedImagePath())
.into(holder.productImageView)
}
inner class ViewHolderProductList(itemView: View) : RecyclerView.ViewHolder(itemView) {
val productImageView = itemView.findViewById<ImageView>(R.id.productImageView_Item)
val productNameTextView = itemView.findViewById<TextView>(R.id.productName_textView_item)
}
}
I recently did this in one of my lists. You want to find out the device's screen width:
val displayMetrics = DisplayMetrics()
(context as NavigationActivity).windowManager.getDefaultDisplay().getMetrics(displayMetrics)
width = displayMetrics.widthPixels
And then divide the width of the device in order to have 3 and a 1/3 blocks cover it, so divide the screen width by around 3.33:
itemWidth = width / 3.33
Now on your onBind method in your list's adapter, you want to change the item's container layout params width to be the itemWidth:
val lp = itemView.container.layoutParams
lp.height = originalHeight
lp.width = itemWidth
itemView.container.layoutParams = lp
You might need to take into consideration padding between items, so instead of:
itemWidth = width / 3.33
Something like:
itemWidth = (width - totalPadding) / 3.33
EDIT
Add it in your code like this:
class ProductListAdapter(val context: Context, val products: List<Product>) : RecyclerView.Adapter<ProductListAdapter.ViewHolderProductList>() {
// holds this device's screen width,
private var screenWidth = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderProductList {
// get screen width
val displayMetrics = DisplayMetrics()
(context as YourActivity).windowManager.getDefaultDisplay().getMetrics(displayMetrics)
screenWidth = displayMetrics.widthPixels
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.item_product_list,parent, false)
return ViewHolderProductList(cellForRow)
}
override fun getItemCount(): Int {
return products.size
}
override fun onBindViewHolder(holder: ViewHolderProductList, position: Int) {
val product = products[position]
val itemWidth = screenWidth / 3.33
val lp = holder.cardView.layoutParams
lp.height = lp.height
lp.width = itemWidth
itemView.container.layoutParams = lp
holder.productNameTextView.text = product.name
Glide
.with(context)
.load(product.getFormattedImagePath())
.into(holder.productImageView)
}
inner class ViewHolderProductList(itemView: View) : RecyclerView.ViewHolder(itemView) {
val productImageView = itemView.findViewById<ImageView>(R.id.productImageView_Item)
val productNameTextView = itemView.findViewById<TextView>(R.id.productName_textView_item)
}
}

Recyclerview missing item when element's width and height is enlarged

I've been scratching my head about this problem. I have a recyclerview inside a recyclerview that shows a grid layout where you can scroll both vertically and horizontally.
I decided to enlarge the width and height of each item. Previously, width and height were: 80 x 100
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="80dp"
android:layout_height="100dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
And then I enlarged it to 150x 180. And then Level 4-6 row completely disappeared no matter how much I scroll up. I only expected the recyclerview to display larger images. Why does this happen?
[EDIT] I ran a debug and it seems only 3 positions are bound in the outer adapter when the app is run on a phone but there are 4 positions when run on a tablet.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="150dp"
android:layout_height="180dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
Please note that this also happens when I run the app on a device. In a tablet, it shows all the Levels, while on a phone, it only shows Levels 1-3.
Outer recyclerview layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mainlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/swiperefresh"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/toolbar">
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="always">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/browsebooksheader_layout"
android:id="#+id/bb_header"
app:layout_constraintBottom_toTopOf="#+id/guidelinetop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guidelinetop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.30"/>
<Button
android:id="#+id/btn_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#drawable/search_btn_border"
android:padding="8dp"
android:text="Search"
android:textSize="#dimen/browse_search_textsize"
android:textColor="#android:color/darker_gray"
app:layout_constraintBottom_toTopOf="#+id/recyclerview_main"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bb_header" />
<ProgressBar
android:id="#+id/main_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_marginTop="16dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:indeterminateTint="#color/color_grey"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/swiperefresh"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<include layout="#layout/toolbar_layout_wo_learn"/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Inner recyclerview layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="wrap_content">
<TextView
android:id="#+id/book_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Category Title"
android:textColor="#color/defaultdark_color"
android:textSize="#dimen/browse_category_textsize"
android:fontFamily="#font/vagroundedstd_bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="#dimen/browse_seeall_imagesize"
android:layout_height="#dimen/browse_seeall_imagesize"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
android:id="#+id/see_all_btn"
android:src="#drawable/seeall"
app:layout_constraintBottom_toTopOf="#+id/inner_recyclerview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="#+id/inner_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/book_category"/>
<TextView
android:id="#+id/h_line"
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#color/color_grey"
android:gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:alpha="0.5"
app:layout_constraintTop_toBottomOf="#id/inner_recyclerview"/>
</android.support.constraint.ConstraintLayout>
Outer Adapter
class BrowseBooksAdapter : RecyclerView.Adapter<BrowseBooksAdapter.SimpleViewHolder>() {
private var sortedList : SortedMap<String, List<ResourcesList>> = emptyMap<String, List<ResourcesList>>().toSortedMap()
fun setData(sortedList : SortedMap<String, List<ResourcesList>>) {
if (this.sortedList != sortedList) {
this.sortedList = sortedList
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return sortedList.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.browsebooks_layout, parent, false)
return SimpleViewHolder(view)
}
override fun onBindViewHolder(holder: SimpleViewHolder, position: Int) {
val dataList = ArrayList(sortedList.values)[position]
holder.category_text.text = ArrayList(sortedList.keys)[position]
holder.booksByCategory = dataList
holder.horizontalAdapter.setData(dataList)
}
class SimpleViewHolder(v: View, var booksByCategory : List<ResourcesList>? = null) : RecyclerView.ViewHolder(v) {
var recyclerView : RecyclerView = v.findViewById(R.id.inner_recyclerview)
var category_text : TextView = v.findViewById(R.id.book_category)
var fragmentCallback: FragmentCallback? = null
val horizontalAdapter: BooksByCategoryAdapter
init {
recyclerView.layoutManager = object : LinearLayoutManager(v.context, LinearLayoutManager.HORIZONTAL, false) {
override fun canScrollHorizontally(): Boolean {
return true
}
override fun canScrollVertically(): Boolean {
return false
}
}
horizontalAdapter = BooksByCategoryAdapter()
recyclerView.adapter = horizontalAdapter
fragmentCallback = v.context as FragmentCallback
var mLastClickTime : Long = 0
v.see_all_btn.setOnClickListener {
val category = v.book_category.text.toString()
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackSeeAllFragment(booksByCategory!!, category)
}
}
}
}
}
}
Inner Adapter
class BooksByCategoryAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var booksByCategory: List<ResourcesList> = emptyList()
fun setData(data: List<ResourcesList>) {
if (this.booksByCategory != data) {
this.booksByCategory = data
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return booksByCategory.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.bookcontainer_layout, parent, false)
return CustomVH(cellForRow)
}
override fun onBindViewHolder(rawHolder: RecyclerView.ViewHolder, position: Int) {
val holder = rawHolder as CustomVH
val booksProcessModel = BooksProcessModel()
booksProcessModel.setItem(booksByCategory[position])
holder.booksByCategory = booksByCategory[position]
Picasso.get().load(booksByCategory[position].coverURI).into(holder.inner_bookcover)
}
private inner class CustomVH(v: View, var booksByCategory : ResourcesList? = null): RecyclerView.ViewHolder(v) {
var inner_bookcover : ImageView = v.findViewById(R.id.inner_bookcover)
var fragmentCallback: FragmentCallback? = null
var ctx : Context = v.context
var mLastClickTime : Long = 0
init {
fragmentCallback = ctx as FragmentCallback
v.setOnClickListener {
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackDownloadBookFragment(booksByCategory!!)
}
}
}
}
}
class BooksProcessModel {
private var processBooks : ResourcesList? = null
fun setItem(books: ResourcesList) {
this.processBooks = books
}
fun setVisibility(ctx: Context) : Int {
val sp = PreferenceManager.getDefaultSharedPreferences(ctx)
val page = sp.getString(processBooks!!.id.toString(), "")
if (page != "") {
return View.VISIBLE
}
return View.INVISIBLE
}
}
}
You need to change the layout_height of the outer recycler view to wrap_content.
Nevermind, figured it out. I should be using a NestedScrollView instead of a ScrollView. The missing items were still there. It's just that I couldn't scroll the nested recyclerview.
In my case it was because I had the RecyclerView directly and even though it scrolled (horizontally), the same thing happened to me if for example the keyboard appeared.
Solution: Put the RecyclerView inside a NestedScrollView and delegate the scroll to it, like this:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/item"/>
</androidx.core.widget.NestedScrollView>

Categories

Resources