I have Recyclerview multiple view types and have Recyclerview in Recyclerview when scroll lag first time I do not know why scroll lag first time
here my layout fragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.LottoFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/teal_700"
android:padding="20dp">
<LinearLayout
android:id="#+id/searchLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/menuTextInputLayout"
style="#style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/label">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="#+id/contentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:behavior_peekHeight="56dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/ic_list_header_background"
android:clickable="true"
android:elevation="4dp"
android:orientation="horizontal"
android:padding="20dp">
<TextView
android:id="#+id/lotto_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/kanit_light"
android:text="0 items(s)" />
<ImageView
android:id="#+id/filterIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:src="#android:drawable/arrow_up_float"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:id="#+id/front_layer_linear_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/lotto_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="64dp"
tools:itemCount="3"
tools:listitem="#layout/item_lotto_prizes_one" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
here my fragment
lottoRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = lottoLatestAdapter
}
here my Adapter:
class LottoLatestAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val VIEW_TYPE_LOTTO_PRIZE_ONE = 1
private const val VIEW_TYPE_LOTTO_PRIZE_RUNNING_NUMBER = 2
private const val VIEW_TYPE_LOTTO_PRIZE_ONE_NEAR = 3
private const val VIEW_TYPE_LOTTO_PRIZE_OTHER = 4
}
var latestList: MutableList<LottoPrizesLatestModel> = mutableListOf()
set(value) {
field = value
notifyDataSetChanged()
}
private val viewPool : RecycledViewPool = RecycledViewPool()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
VIEW_TYPE_LOTTO_PRIZE_ONE -> {
LottoPrizeOneHolder(ItemLottoPrizesOneBinding.inflate(inflater, parent, false))
}
VIEW_TYPE_LOTTO_PRIZE_RUNNING_NUMBER -> {
LottoPrizeRunningNumberHolder(
ItemLottoRunningNumbersBinding.inflate(
inflater,
parent,
false
)
)
}
VIEW_TYPE_LOTTO_PRIZE_ONE_NEAR -> {
LottoPrizeOneNearHolder(
ItemLottoPrizesOneNearBinding.inflate(
inflater,
parent,
false
)
)
}
VIEW_TYPE_LOTTO_PRIZE_OTHER -> {
LottoPrizeOtherListHolder(
ItemLottoPrizesOtherListBinding.inflate(
inflater,
parent,
false
),
LottoLatestPrizeOtherAdapter()
)
}
else -> throw NullPointerException("Not have view Type")
}
}
override fun getItemViewType(position: Int): Int {
return when (latestList[position]) {
is LottoPrizesLatestModel.LottoLatestPrizeOne -> VIEW_TYPE_LOTTO_PRIZE_ONE
is LottoPrizesLatestModel.LottoLatestRunningNumbers -> VIEW_TYPE_LOTTO_PRIZE_RUNNING_NUMBER
is LottoPrizesLatestModel.LottoLatestPrizeFirstNear -> VIEW_TYPE_LOTTO_PRIZE_ONE_NEAR
is LottoPrizesLatestModel.LottoLatestPrizeOther -> VIEW_TYPE_LOTTO_PRIZE_OTHER
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is LottoPrizeOneHolder -> holder.bind(latestList[position] as LottoPrizesLatestModel.LottoLatestPrizeOne)
is LottoPrizeRunningNumberHolder -> holder.bind(latestList[position] as LottoPrizesLatestModel.LottoLatestRunningNumbers)
is LottoPrizeOneNearHolder -> holder.bind(latestList[position] as LottoPrizesLatestModel.LottoLatestPrizeFirstNear)
is LottoPrizeOtherListHolder -> {
viewPool.putRecycledView(holder)
holder.bind(latestList[position] as LottoPrizesLatestModel.LottoLatestPrizeOther, viewPool)
}
}
}
override fun getItemCount(): Int = latestList.size
}
here my layout holder
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_margin="8dp">
<TextView
android:id="#+id/title_prize_other_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/kanit_light"
android:gravity="center"
android:textSize="26sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/prize_other_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_prize_other_text_view"
tools:itemCount="10"
tools:listitem="#layout/item_lotto_prizes_other" />
<GridLayout
android:id="#+id/prize_other_grip_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_prize_other_text_view" />
<TextView
android:id="#+id/text_prize_one_near_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/kanit_light"
android:text="#string/lotto_prize_per_baht"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/prize_other_recycler_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
and here my holder recyclerView in RecyclerView
class LottoPrizeOtherListHolder(
private val binding: ItemLottoPrizesOtherListBinding,
private val lottoLatestPrizeOtherAdapter: LottoLatestPrizeOtherAdapter
) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
lottoLatestPrizeOther: LottoPrizesLatestModel.LottoLatestPrizeOther,
viewPool: RecyclerView.RecycledViewPool
) = with(binding) {
prizeOtherRecyclerView.apply {
layoutManager = GridLayoutManager(
context,
2,
GridLayoutManager.VERTICAL,
false
).apply {
initialPrefetchItemCount = lottoLatestPrizeOther.prizeModel.number.size
}
this.adapter = lottoLatestPrizeOtherAdapter
setRecycledViewPool(viewPool)
}
lottoLatestPrizeOtherAdapter.numberList =
lottoLatestPrizeOther.prizeModel.number.toMutableList()
titlePrizeOtherTextView.text = lottoLatestPrizeOther.prizeModel.name
textPrizeOneNearTextView.text = itemView.context.getString(
R.string.lotto_prize_per_baht,
lottoLatestPrizeOther.prizeModel.reward
)
}
}
my problem is : When i scroll recyclerview for first time it is not smooth and logcat show recyclerview Skipped 51 frames! adapter but when I reach end of recyclerView and scroll to up then scroll down it is very smooth and my recyclerview have TextView Only !!! how can I solve this issue?
this my video App https://youtu.be/Li7aKWmEfXQ
In your fragment add this after applying LayoutManager:
lottoRecyclerView.setDrawingCacheEnabled(true);
lottoRecyclerView.setItemViewCacheSize(myCacheSize);
The main reason Jank occurs the first time because on the first time it is loading the values onto memory dynamically. while once it is down it already has a few elements pre loaded
You can handle it using
mRecyclerView.setHasFixedSize(true);
mRecyclerview.setNestedScrollingEnabled(false);
in your kotlin code
or add android:nestedScrollingEnabled="false" in your RecyclerView xml
You can reach some performance improvements by applying the following:
recyclerView.setHasFixedSize(true);
recyclerView.setDrawingCacheEnabled(true);
..in combination with lite mode of MapsView:
<com.google.android.gms.maps.MapView
android:layout_width="match_parent"
android:layout_height="200dp"
app:liteMode="true"
app:mapType="normal" />
For it was not an option to disable nestedScrollingEnabled as mentioned by #Narendra_Nath, which further improves performance.
Related
I'm trying to display a list of files and folders using a RecyclerView.
Here is the Adapter and the ViewHolder :
class FilesRecyclerViewAdapter(private val files: Array<File>) : RecyclerView.Adapter<FilesRecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_file_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = files[position]
holder.icon.setImageResource(if (item.isDirectory) R.drawable.folder else R.drawable.file)
holder.fileName.text = item.name
}
override fun getItemCount(): Int = files.size
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val icon: ImageView = view.findViewById(R.id.icon)
val fileName: TextView = view.findViewById(R.id.fileName)
}
}
The layout for a single item in the list (R.layout.fragment_file_item) :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fileItem"
android:layout_width="match_parent"
android:layout_height="#dimen/file_item_height">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_launcher_foreground" />
<TextView
android:id="#+id/fileName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:text="Element"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/icon"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
And this is how it's included into the main activity :
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fileSystem"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Finally the code in the onCreate function of the main activity :
private lateinit var fileSystem: RecyclerView
// Later in the code
fileSystem = findViewById(R.id.fileSystem)
fileSystem.layoutManager = LinearLayoutManager(this)
fileSystem.adapter = FilesRecyclerViewAdapter(Environment.getExternalStorageDirectory().listFiles())
I expected to have a list of items with the icon on the left and the name of the folder slighly to the right like on the item layout preview :
Instead, I have this mess :
What did I do wrong ?
app:layout_constraintHorizontal_bias="0.5" makes your RecyclerView start on 50% of view.
Remove it, if you dont need them
I have the following layout file -
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/fragment_marketplace_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/fragment_marketplace_appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="220dp"
android:fitsSystemWindows="true"
android:minHeight="150dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:titleEnabled="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:layout_collapseParallaxMultiplier="0">
<com.twoverte.views.ClearableAutoCompleteTextView
android:id="#+id/fragment_marketplace_searchview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:completionThreshold="0"
android:hint="#string/fragment_marketplace_search_hint"
android:iconifiedByDefault="false"
android:inputType="text|textAutoCorrect"
android:maxLength="25"
android:textIsSelectable="false"
android:textSelectHandle="#xml/empty_shape"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp" />
<TextView
android:id="#+id/fragment_marketplace_discover_products_from_myverte_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="15dp"
android:fontFamily="#font/noto_sans"
android:text="#string/fragment_marketplace_discover_products_from_myverte"
android:textSize="17sp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fragment_marketplace_vendors_recycler_view"
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_gravity="bottom"
android:minHeight="40dp"
android:orientation="horizontal"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="1"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/fragment_marketplace_vendor_row_item" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="#+id/fragment_marketplace_featured_products_nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/fragment_marketplace_featured_products_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/very_light_grey"
android:fontFamily="#font/noto_sans"
android:paddingStart="14dp"
android:paddingLeft="14dp"
android:paddingTop="15dp"
android:paddingEnd="14dp"
android:text="#string/fragment_marketplace_featured_products"
android:textSize="17sp"
android:visibility="gone"
tools:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fragment_marketplace_products_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="#color/very_light_grey"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:itemCount="2"
tools:listitem="#layout/fragment_marketplace_products_row_item" />
<View
android:id="#+id/activity_product_page_bottom_view"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_marginTop="60dp"
android:background="#color/light_black"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
and inside my onViewCreated I have the following code -
import kotlinx.android.synthetic.main.fragment_marketplace.fragment_marketplace_vendors_recycler_view as vendorsList
...
...
// onViewCreated ->
val originalVendorListHeight: Int = vendorsList.layoutParams?.height!!
appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, value ->
vendorsList.updateLayoutParams {
this.height = originalVendorListHeight + value
(this as CollapsingToolbarLayout.LayoutParams).setMargins(0, 0, 0, abs(value))
}
})
Which creates an animation of the top horizontal RV of it being expanding with it's height up and down up to a cap which is what I need.
The issue I am facing now is that each ViewHolder inside that RV is not proportional once expanded as I am only changing it's height and not width and I am facing an issue trying to set the width of each individual VH as I can't rely on it's parent size as before with the height.
Here is my adapter and VH -
class VendorAdapter(private val miniVendorModels: List<MiniVendorModel>, private val context: Context) : RecyclerView.Adapter<VendorsHolder>() {
companion object {
const val EXTRA_VENDOR_MODEL = "EVM"
}
private val vendorsHoldersList = mutableListOf<VendorsHolder>()
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): VendorsHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.fragment_marketplace_vendor_row_item, viewGroup, false)
val vendorsHolder = VendorsHolder(view)
vendorsHoldersList.add(vendorsHolder)
return vendorsHolder
}
override fun onBindViewHolder(vendorsHolder: VendorsHolder, i: Int) {
val model = miniVendorModels[i]
Picasso.get().load(model.bannerPicture).into(vendorsHolder.vendorImageView)
vendorsHolder.vendorImageView.setOnClickListener { v: View? ->
try {
val intent = Intent(context, VendorPageActivity::class.java)
intent.putExtra(EXTRA_VENDOR_MODEL, model)
context.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(context, ResourceHelper.getString(R.string.marketplace_vendor_unavailable), Toast.LENGTH_SHORT).show()
}
}
}
override fun getItemCount(): Int = miniVendorModels.size
}
class VendorsHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var vendorImageView: ImageView = itemView.findViewById(R.id.vendors_row_item_imageview)
var rootLayout: ConstraintLayout = itemView.findViewById(R.id.vendors_row_item_root_layout)
var vendorHolderCardview: CardView = itemView.findViewById(R.id.search_image_contact_cardview)
}
<?xml version="1.0" encoding="utf-8"?>
<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/vendors_row_item_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:id="#+id/search_image_contact_cardview"
android:layout_width="152dp"
android:layout_height="match_parent"
android:layout_margin="5dp"
app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/vendors_row_item_imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:src="#mipmap/ic_launcher" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
As you can see, the ViewHolders are Match Parent in height meaning that they will expand with their parents.
I can't use the same trick for the width as it is a horizontal RV, so I will need to use a different method to enable the expanding and reducing all of the RV's VH width.
solved -
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): VendorsHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.fragment_marketplace_vendor_row_item, viewGroup, false)
val vendorsHolder = VendorsHolder(view)
vendorsHoldersList.add(vendorsHolder)
vendorsHolder.rootLayout.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
vendorsHolder.rootLayout.updateLayoutParams {
this.width = 152 + bottom
}
}
})
return vendorsHolder
}
where 152 is my base width of the viewholder
I have this layout and am trying to switch on a corresponding LL based on a position. However the first Linear Layout will only show up and be drawn if I give the text view inside an absolute dimension such as 200dp for the width, I have tried 0dp with constraints as well.
<?xml version="1.0" encoding="utf-8"?>
<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/draft_rider_container"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="lost"
android:textColor="#android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<LinearLayout
android:id="#+id/section"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone">
<TextView
android:id="#+id/fake"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
tools:text="Andrea Dovizioso's"></TextView>
</LinearLayout>
<LinearLayout
android:id="#+id/row"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone">
<TextView
android:id="#+id/rider_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
tools:text="Andrea Dovizioso's"></TextView>
</LinearLayout>
This layout is rendered from a recycler view that is on my fragment which is placed like this :
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".roster.RosterFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/player_rider_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#ffffff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:src="#drawable/baseline_edit_white_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:rippleColor="#color/colorPrimary" />
And here is my adapter :
class PlayerRidersAdapter : RecyclerView.Adapter<PlayerRidersAdapter.ViewHolder>() {
var riderList = ArrayList<Rider>()
override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.rider_item_layout, null, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return riderList.count() + 2
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when(position) {
0 -> showHeader(holder)
3 -> showSection(holder)
else -> showRow(holder)
}
}
fun showHeader(holder: ViewHolder) {
holder.headerContainer.visibility = View.VISIBLE
holder.sectionContainer.visibility = View.GONE
holder.rowContainer.visibility = View.GONE
}
fun showRow(holder: ViewHolder) {
holder.headerContainer.visibility = View.GONE
holder.sectionContainer.visibility = View.GONE
holder.rowContainer.visibility = View.VISIBLE
//holder.name.text = rider.name
}
fun showSection(holder: ViewHolder) {
holder.headerContainer.visibility = View.GONE
holder.sectionContainer.visibility = View.VISIBLE
holder.rowContainer.visibility = View.GONE
}
fun addRiders(riders : ArrayList<Rider>){
riderList.addAll(riders)
notifyDataSetChanged()
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var headerContainer = view.findViewById<LinearLayout>(R.id.header)
var sectionContainer = view.findViewById<LinearLayout>(R.id.section)
var rowContainer = view.findViewById<LinearLayout>(R.id.row)
var name = view.findViewById<TextView>(R.id.rider_name)
}
}
In your adapter, specify the following in onCreateViewHolder():
val view = LayoutInflater.from(parent.context).inflate(R.layout.rider_item_layout, parent, false)
Specifying the root argument will let the inflated layout be sized properly. This is most likely your problem.
However, match_parent sometimes has strange behavior when used in ConstraintLayout, so use 0dp instead and remember to constrain the effected sides. It would be nice for ConstraintLayout to either handle this case, report it as an error or, at least, support a Lint check.
An aside: The constraints on the TextViews that are held within a LinearLayout have no effect. Constraints only apply to direct children of ConstraintLayout.
I am trying to make a bottom sheet with a recyclerview with cards. I was able to put the the cards within the bottom sheet but I am having problems with the recyclerview. As shown in the photo, it creates multiple bottom sheets with the cards within them. I have tried to fix this but so far have no luck. How can I make it so that the bottom sheet contains the cards with a recyclerview? I believe the problem is the Recyclerview in activity_main but don't know where to place it.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (supportActionBar != null)
supportActionBar?.hide()
val modelList = readFromAsset()
val adapter = CustomAdapter(modelList, this)
rcv.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) as RecyclerView.LayoutManager?
rcv.adapter = adapter;
configureBackdrop()
}
private var mBottomSheetBehavior: BottomSheetBehavior<View?>? = null
private fun configureBackdrop() {
// Get the fragment reference
val fragment = supportFragmentManager.findFragmentById(R.id.filter_fragment)
fragment?.let {
// Get the BottomSheetBehavior from the fragment view
BottomSheetBehavior.from(it.view)?.let { bsb ->
mBottomSheetBehavior = bsb
}
}
}
private fun readFromAsset(): List<Model> {
val modeList = mutableListOf<Model>()
val bufferReader = application.assets.open("android_version.json").bufferedReader()
val json_string = bufferReader.use {
it.readText()
}
val jsonArray = JSONArray(json_string);
for (i in 0..jsonArray.length() - 1) {
val jsonObject: JSONObject = jsonArray.getJSONObject(i)
val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
modeList.add(model)
}
return modeList
}
}
CustomAdapter.kt
class CustomAdapter(val modelList: List<Model>, val context: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).bind(modelList.get(position));
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolder(layoutInflater.inflate(R.layout.backdrop_fragment, parent, false))
}
override fun getItemCount(): Int {
return modelList.size;
}
lateinit var mClickListener: ClickListener
fun setOnItemClickListener(aClickListener: ClickListener) {
mClickListener = aClickListener
}
interface ClickListener {
fun onClick(pos: Int, aView: View)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
mClickListener.onClick(adapterPosition, itemView)
}
fun bind(model: Model): Unit {
itemView.txt.text = model.name
itemView.sub_txt.text = model.version
val id = context.resources.getIdentifier(model.name.toLowerCase(), "drawable", context.packageName)
itemView.img.setBackgroundResource(id)
}
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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">
<TextView
android:id="#+id/textView"
android:text="#string/main_activity_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:id="#+id/rcv"/>
<fragment
app:behavior_hideable="false"
app:behavior_peekHeight="100dp"
android:layout_marginTop="?attr/actionBarSize"
app:behavior_skipCollapsed="false"
android:id="#+id/filter_fragment"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:name="behavior.sheet.bottom.BackdropFragment" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
backdrop_fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/backdrop_fragment_background"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/backdrop_content" />
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="75dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
card_view:cardCornerRadius="30dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="false"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
card_view:contentPadding="10dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="#+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:id="#+id/img"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_toRightOf="#+id/txt"
android:contentDescription="#string/app_name" />
<TextView
android:id="#+id/sub_txt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_toRightOf="#+id/img"
android:autoSizeMaxTextSize="8sp"
android:autoSizeMinTextSize="6sp"
android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform"
android:text="Title" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
Please set the Bottom sheet Linear Layout to wrap-content
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/backdrop_fragment_background"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/backdrop_content" />
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="75dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
card_view:cardCornerRadius="30dp"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="false"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
card_view:contentPadding="10dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="#+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:id="#+id/img"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_toRightOf="#+id/txt"
android:contentDescription="#string/app_name" />
<TextView
android:id="#+id/sub_txt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_toRightOf="#+id/img"
android:autoSizeMaxTextSize="8sp"
android:autoSizeMinTextSize="6sp"
android:autoSizeStepGranularity="2sp"
android:autoSizeTextType="uniform"
android:text="Title" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
You have the RecyclerView in the activity_main layout file instead of inside the Fragment layout file.
Here is what you should do, create a class that inherits BottomSheetDialogFragment(). See below.
Add dependency implementation 'com.google.android.material:material:1.2.0-alpha04' inside the app/build.gradle.
Create your bottom sheet class
class BottomSheetExampleDialogFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) : View? =
inflater.inflate(R.layout.bottom_sheet_example_dialog_fragment, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Handle RecyclerView here
}
}
Bottom sheet layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rcv"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Then in your MainActivity you can show the bottom sheet by using
val bottomSheetFragment = BottomSheetExampleDialogFragment()
bottomSheetFragment.show(supportFragmentManager, bottomSheetFragment.getTag())
Hi I am working on showing a view (publisher ad) below recycler view. Recycler view has some empty views to show publisher ad when it comes to related index. Currently I can click on any recycler view item and I am able to see my ad, but I cannot click on ad. How to achieve this? Here is my layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
ads:adSize="SMART_BANNER"
ads:adUnitId="#string/parallax_ad_unit_id"/>
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" />
</widgets.AppSwipeRefreshLayout>
</FrameLayout>
As you are using FrameLayout the PublisherAdView will be overlapped by AppRecyclerView or AppSwipeRefreshLayout, so when you click on item, that click event will be listened by `AppRecyclerView1 every time.
Try after changing the position of your RecyclerView and AdView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" />
</widgets.AppSwipeRefreshLayout>
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
ads:adSize="SMART_BANNER"
ads:adUnitId="#string/parallax_ad_unit_id"/>
</FrameLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:nestedScrollingEnabled="false"
android:scrollbars="vertical"
ads:layout_constraintBottom_toBottomOf="parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintTop_toBottomOf="#id/ad_view" />
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="0dp"
ads:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</widgets.AppSwipeRefreshLayout>
Use a RelativeLayout for activity_main.xml which allows to place views
on top of others (in Z axis).
ex: SwipeRefreshLayout is the top most view here.
<RelativeLayout 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"
android:focusable="true"
tools:context=".MainActivity">
<View
android:id="#+id/ad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorAccent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_posts"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_posts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</RelativeLayout>
Create two item layouts for two types of recycler view types
ex:
item_normal.xml, item_transparent.xml (where layout background is transparent that allow to see the layout below the surface area)
Setting android:clickable=false in item_transparent does not stop triggering the click event on transparent item, so use communication flow using interfaces, to bring the other view(ad) to the front when clicked on transparent item.
MainActivity.kt
class MainActivity : AppCompatActivity(), RvAdpater.OnItemClick {
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var adView: View
private lateinit var rvPosts: RecyclerView
override fun onClick() {
bringAdFront()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView()
setupSwipeRefreshLayout()
}
private fun setupRecyclerView() {
rvPosts = findViewById(R.id.rv_posts)
rvPosts.layoutManager = LinearLayoutManager(this)
val rvAdpater = RvAdpater()
rvAdpater.setListener(this)
rvPosts.adapter = rvAdpater
}
private fun setupSwipeRefreshLayout() {
swipeRefreshLayout = findViewById(R.id.swipe_refresh_posts)
swipeRefreshLayout.setOnRefreshListener {
Log.d(TAG, "Refreshing...")
}
}
private fun bringAdFront() {
adView = findViewById<View>(R.id.ad)
adView.bringToFront()
}
// to go back to the normal recycler view(which is inside swipe refresh layout)
// when back button is pressed
override fun onBackPressed() {
val parent = swipeRefreshLayout.parent as ViewGroup
parent.removeAllViews()
parent.addView(adView, 0)
// at last add swipe refresh layout which is the container of the recyclerview
parent.addView(swipeRefreshLayout, 1)
}
}
RvAdapter.kt
const val TAG = "RvAdpater"
class RvAdpater : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private lateinit var listener:OnItemClick
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val viewNormal = LayoutInflater.from(parent.context).inflate(R.layout.item_normal, parent, false)
val viewTransparent = LayoutInflater.from(parent.context).inflate(R.layout.item_transparent, parent, false)
return when(viewType){
0 -> NormalViewHolder(viewNormal)
2 -> TransparentViewHolder(viewTransparent)
else -> NormalViewHolder(viewNormal)
}
}
override fun getItemCount(): Int = 10
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(getItemViewType(position)){
0 -> {
val normalHolder = holder as NormalViewHolder
normalHolder.tv.text = "Post"
normalHolder.itemView.setOnClickListener {
Log.d(TAG, "Clicked on Normal item")
}
}
2 -> {
val transparentHolder = holder as TransparentViewHolder
transparentHolder.itemView.setOnClickListener {
listener.onClick()
}
}
}
}
fun setListener(onItem:OnItemClick){
listener = onItem
}
interface OnItemClick{
fun onClick()
}
override fun getItemViewType(position: Int): Int = position % 2 * 2
class NormalViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val tv:TextView = itemView.findViewById(R.id.tv_post)
}
class TransparentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
}
Checkout branch: with-swipe-layout