I am trying to implement a simple nested recyclerview with a vertical parent and horizontal children.
The layout for the parent is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<include layout="#layout/loading_indicator" />
<include layout="#layout/error_layout" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcasts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/podcastsTitle"
android:layout_marginTop="5dp" />
</RelativeLayout>
The layout for the child recyclerview is:
<?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:layout_marginBottom="10dp"
android:orientation="vertical">
<TextView
android:id="#+id/title"
style="#style/TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<TextView
android:id="#+id/description"
style="#style/AuxTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:maxLines="2"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcastsItems"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
The adapter for the parent is as follows:
class CuratedPodcastsAdapter(val context: Context, private val podcastLists: ArrayList<PodcastList>) : RecyclerView.Adapter<CuratedPodcastsAdapter.ViewHolder>() {
private val viewPool = RecyclerView.RecycledViewPool()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_section_layout, parent, false))
override fun getItemCount(): Int = podcastLists.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(podcastLists[holder.adapterPosition])
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
#BindView(R.id.title)
lateinit var title: TextView
#BindView(R.id.description)
lateinit var description: TextView
#BindView(R.id.podcastsItems)
lateinit var podcastItems: RecyclerView
init {
ButterKnife.bind(this, itemView)
}
fun bindItems(curatedPodcastList: PodcastList) {
title.text = curatedPodcastList.title
description.text = curatedPodcastList.description
title.typeface = FontUtils.boldTypeface
description.typeface = FontUtils.mediumTypeface
val childLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
childLayoutManager.initialPrefetchItemCount = 4
podcastItems.apply {
layoutManager = childLayoutManager
adapter = PodcastAdapter(this#CuratedPodcastsAdapter.context, curatedPodcastList.podcasts!!)
setItemViewCacheSize(20)
setRecycledViewPool(viewPool)
}
}
}
}
while the children's adapter is:
class PodcastAdapter(val context: Context, private val episodes: ArrayList<Podcast>): RecyclerView.Adapter<PodcastAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_item_layout, parent, false))
override fun getItemCount(): Int = episodes.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(episodes[holder.adapterPosition])
}
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
#BindView(R.id.art)
lateinit var art: ImageView
#BindView(R.id.title)
lateinit var title: TextView
#BindView(R.id.publisher)
lateinit var publisher: TextView
#BindView(R.id.card)
lateinit var card: CardView
init {
ButterKnife.bind(this, view)
}
fun bindItems(podcast: Podcast){
title.text = podcast.title
publisher.text = podcast.publisher
GlideApp.with(context).load(podcast.image).error(R.drawable.no_cover).into(art)
card.setOnClickListener {
context.startActivity(context.intentFor<PodcastActivity>(PodcastActivity.PODCAST_ID to podcast.id, PodcastActivity.PODCAST_TITLE to podcast.title))
}
}
}
}
When content is loaded for the first time, everything appears correctly like below:
After scrolling downwards then back up again, spaces begin to appear between the content like so:
None of the recyclerviews have any item decoration added to them. My perception was that using a common viewpool would at least improve performance and help with such layout inconsistencies but it hasn't helped.
What is the cause of this problem and how can it be fixed?
You need to change the height of your child layout of recyclerview
to android:layout_height="wrap_content"
Sample code
<?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="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<TextView
android:id="#+id/title"
style="#style/TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<TextView
android:id="#+id/description"
style="#style/AuxTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:maxLines="2"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcastsItems"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Related
As the official material.io documentation says:
Chips are compact elements that represent an input, attribute, or action.
I want to add chips as attributes (tags) for any blog post, a recyclerview is used to show the list of blog posts:
item_post.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mcv_container_item_blog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
android:background="?selectableItemBackground"
app:cardUseCompatPadding="true">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/llc_container_item_project"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/item_margin"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/aciv_item_post_image"
android:layout_width="match_parent"
android:layout_height="#dimen/item_project_Image_size"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:srcCompat="#drawable/drawer_bg"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/item_margin"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textColorHighlight="#color/design_default_color_background"
android:text="Chameleon UI Kit" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_date"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/red_lighten"
android:layout_marginTop="#dimen/user_name_margin_start"
android:layout_marginEnd="#dimen/user_name_margin_start"
android:layout_marginBottom="#dimen/user_name_margin_start"
android:text="10 Sept"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_short_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
android:text="#string/about_me" />
<include layout="#layout/item_divider"/>
<HorizontalScrollView
android:id="#+id/hsv_item_post_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:id="#+id/cg_item_post_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true"/>
</HorizontalScrollView>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
then the PostAdapter class:
class PostAdapter(
private val postList: List<Post>
): RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_post, parent, false)
return PostViewHolder(view)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val post = postList[position]
holder.postTitle.text = post.postTitle
holder.postDescription.text = post.postDescription
holder.postDate.text = post.postDate
val tagChip = Chip(holder.itemView.context).apply {
id = View.generateViewId()
text = "label"
}
holder.postTagsChipGroup.addView(tagChip)
}
override fun getItemCount(): Int {
return postList.size
}
inner class PostViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postTitle: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_title)
val postDescription: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_short_description)
val postTagsChipGroup: ChipGroup = itemView.findViewById(R.id.cg_item_post_tags)
val postDate: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_date)
}
}
at this stage a weird thing happens, when I 'scroll recyclerview' other Chips are added from nowhere as you can see this behavior in gif below:
how to avoid such behavior, what changes should I make in adapters onBindViewHolder method?
I am facing an strange behaviour now days. I am using view binding for refrencing ids. When RecyclerView height is wrap_content the item display perfectly but when I set recyclerview height match_parent the item width is automatically wrapped (wrap_content) in run time but in xml I have set item width is match_parent.
Please help me to solve this problem.
Please look at my xml and kt files -:
<LinearLayou ------
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idRvTrainerReview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
row_item
<?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="wrap_content"
android:layout_marginTop="#dimen/_1sdp"
android:background="#color/colorWhite"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="#dimen/_10sdp">
<TextView
android:id="#+id/idTvDate"
style="#style/MediumText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="14 feb"
android:includeFontPadding="false"
android:textColor="#color/colorBlack"
android:textStyle="bold" />
<TextView
android:id="#+id/idTvStartTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:gravity="center"
android:includeFontPadding="false"
android:text="19:30" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_right_arrow" />
</LinearLayout>
adapter file -:
class FeedbackDataAdapter(
private var mcontext: Context,
private var mItemList: MutableList<ResponseTrainerFeedback.Result.Training>,
var mClickListener: OnClickListener?
) : RecyclerView.Adapter<TrainingDataViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrainingDataViewHolder {
var layoutInflater = mcontext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as (LayoutInflater)
val rowEventMemberBinding =
RowEventDataBinding.inflate(layoutInflater, parent, false)
return TrainingDataViewHolder(rowEventMemberBinding)
}
override fun getItemCount(): Int = mItemList.size
override fun onBindViewHolder(holder: TrainingDataViewHolder, position: Int) {
holder.mBinding.idTvDate.text = mItemList[holder.adapterPosition].trainingName
holder.mBinding.idTvStartTime.invisible()
holder.itemView.setOnClickListener {
if(mItemList.size > 0){
mClickListener?.onItemClick(holder.adapterPosition)
}
}
}
fun refreshList(mFeedbackList: MutableList<ResponseTrainerFeedback.Result.Training>) {
this.mItemList = mFeedbackList
notifyDataSetChanged()
}
}
class TrainingDataViewHolder(var mBinding: RowEventDataBinding) :
RecyclerView.ViewHolder(mBinding.root) {
}
I've tried your code and It's working.
I've only refactored your adapter according to Android training and if you want to do the codelab: https://codelabs.developers.google.com/codelabs/kotlin-android-training-diffutil-databinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrainingDataViewHolder {
return TrainingDataViewHolder.from(parent)
}
override fun getItemCount(): Int = mItemList.size
override fun onBindViewHolder(holder: TrainingDataViewHolder, position: Int) {
holder.bind(mItemList[position])
holder.itemView.setOnClickListener {
if(mItemList.size > 0){
mClickListener?.onItemClick(holder.adapterPosition)
}
}
}
fun refreshList(mFeedbackList: MutableList<ResponseTrainerFeedback.Result.Training>) {
this.mItemList = mFeedbackList
notifyDataSetChanged()
}
}
class TrainingDataViewHolder(var mBinding: RowEventDataBinding) :
RecyclerView.ViewHolder(mBinding.root) {
fun bind(data: YOURDATA) {
mBinding.idTv = data // it'll map automatically all the property, replace idTv by the name of your variable in your layout
mBinding.idTvStartTime.invisible()
}
companion object {
fun from(parent: ViewGroup): TrainingDataViewHolder {
return TrainingDataViewHolder(RowEventDataBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
}
}
row_item
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="idTv"
type="package.name.to.data" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:background="#android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/idTvDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="#{idTv.idTvDate}"
android:includeFontPadding="false"
android:textColor="#android:color/black"
android:textStyle="bold"
tools:text="14 feb"/>
<TextView
android:id="#+id/idTvStartTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:gravity="center"
android:includeFontPadding="false"
android:text="19:30" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/arrow_up_float" />
</LinearLayout>
</layout>
PS: I don't see <layout> in your row_item, are you sure you're doing binding correctly ?
I'm having a problem inflating my recyclerView inside a BottomSheetDialog using activites.
I keep getting the error:
java.lang.IllegalStateException: recycler_provider must not be null
my method to open the dialog is:
private fun openOptionsDialog(context: Context) {
val dialog = BottomSheetDialog(context)
val layoutInflater: View =
LayoutInflater.from(context).inflate(R.layout.providers_pop_up, null)
dialog.setContentView(layoutInflater)
dialog.show()
recycler_provider.adapter = ProviderAdapter(getProvidersList())
recycler_provider.layoutManager = LinearLayoutManager(this)
recycler_provider?.setHasFixedSize(true)
layoutInflater.close.setOnClickListener {
dialog.dismiss()
}
}
My adapter class:
class ProviderAdapter(private val items: ArrayList<ProviderModel>) : RecyclerView.Adapter<ProviderAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProviderAdapter.ViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.provider_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ProviderAdapter.ViewHolder, position: Int) {
holder.providerTitle.text = items[position].providerName
holder.providerImage.setImageResource(items[position].providerImage)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val providerTitle: TextView = itemView.providerItemName
val providerImage: ImageView = itemView.providerItemImage
}
and the XML code for the provider 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:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/providerRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.002">
<ImageView
android:id="#+id/providerItemImage"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:src="#drawable/meo"></ImageView>
<TextView
android:id="#+id/providerItemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="MEO"
android:textSize="21sp">
</TextView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Anyone knows where the problem is?
the problem there is you haven't initialize recycler_provider
recycler_provider = layoutInflater.findViewById<RecyclerView>(R.id.your_recyclerview_id)
Now I writing my small game "Bulls and Cows". I added to the bottom of my layout recycler view and write sample code to test it:
val adapter = MyAdapter(this, null)
turnStory.adapter = adapter
repeat(100) {
adapter.updateData(Triple("test$it", "test$it", "test$it")
}
I started my activity on the phone and saw that
But when I scrolled down and scrolled back everything became normal
MyAdapter.kt
class MyAdapter(private val ctx: Context, private val data: Triple<String, String, String>?) :
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private val dataset = mutableListOf<Triple<String, String, String>>()
init {
if (data != null) dataset.add(data)
}
class MyViewHolder(val frameLayout: FrameLayout) : RecyclerView.ViewHolder(frameLayout)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val frameLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.frame_layout, parent, false) as FrameLayout
return MyViewHolder(frameLayout)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.frameLayout.numberView.text = dataset[position].first
holder.frameLayout.bullsView.text =
ctx.resources.getString(R.string.bulls_counter, dataset[position].second)
holder.frameLayout.cowsView.text =
ctx.resources.getString(R.string.cows_counter, dataset[position].third)
}
override fun getItemCount(): Int = dataset.size
fun updateData(data: Triple<String, String, String>) {
dataset.add(data)
notifyDataSetChanged()
}
}
frame_layout.xml
<FrameLayout 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="100dp">
<TextView
android:id="#+id/numberView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center"
android:maxLines="1"
android:singleLine="false"
android:textColor="#000000"
android:textSize="36sp"
android:textStyle="bold"
app:autoSizeTextType="none" />
<TextView
android:id="#+id/bullsView"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="end"
android:gravity="center"
android:maxLines="1"
android:singleLine="false"
android:textColor="#000000"
android:textSize="24sp" />
<TextView
android:id="#+id/cowsView"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_gravity="bottom|end"
android:gravity="center"
android:maxLines="1"
android:singleLine="false"
android:textColor="#000000"
android:textSize="24sp" />
Why first elements in recycler view displayed incorrectly before I scroll down?
computer_game_activity.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GameComputerActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/turnStory"
android:layout_width="0dp"
android:layout_height="500dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
I'm currently trying to make a scrollable grid of ScratchViews, but the views that have already been scratched by the user, are appearing as not scratched when the user scrolls down and back up again, because the adapter is repainting each of the views back to their original unscratched state.
I've tried the solutions posted here: I want my RecyclerView to not recycle some items, but preventing the views from being recycled apparently doesn't prevent them from being redrawn.
Here is my code:
Fragment:
class ScratchOffGameFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_scratch_off_game, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var data = ArrayList<ScratchOffGameItem>()
for (i in 1..20) {
var aux = ScratchOffGameItem()
data.add(aux)
}
scratchOffGameRecycle.layoutManager = GridLayoutManager(context, 3)
val scratchOffGameAdapter = ScratchOffGameAdapter(activity!!,data)
scratchOffGameRecycle.recycledViewPool.setMaxRecycledViews(1,0);
scratchOffGameRecycle.adapter = scratchOffGameAdapter
}
Adapter:
class ScratchOffGameAdapter(var context: Context, private val mData: ArrayList<ScratchOffGameItem>) : RecyclerView.Adapter<ScratchOffGameAdapter.ViewHolder>() {
private val mInflater: LayoutInflater = LayoutInflater.from(context)
inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView)
#NonNull
override fun onCreateViewHolder(#NonNull parent: ViewGroup, viewType: Int): ViewHolder {
val view = mInflater.inflate(R.layout.scratch_off_game_item, parent, false)
return ViewHolder(view)
}
// binds the data to the TextView in each cell
override fun onBindViewHolder(#NonNull holder: ViewHolder, position: Int) {
holder.setIsRecyclable(false)
}
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
ScratchoffController(context)
.setThresholdPercent(0.80)
.setTouchRadiusDip(context, 30)
.attach(holder.itemView.scratch_view, holder.itemView.scratch_view_behind)
}
// total number of cells
override fun getItemCount(): Int {
return mData.size
}
override fun getItemViewType(position: Int): Int {
return 1
}
}
Item being painted by the adapter (scratch_off_game_item.xml):
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/achievementItem"
android:layout_width="110dp"
android:layout_height="90dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="30dp"
android:layout_marginTop="20dp"
android:layout_marginRight="30dp"
android:background="#drawable/achievements_border">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RelativeLayout
android:id="#+id/scratch_view_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/zing_rust_demo_base"
android:gravity="center"
android:text="20"
android:textColor="#color/black"
android:textSize="24sp"
android:layout_centerInParent="true"/>
</RelativeLayout>
<com.jackpocket.scratchoff.views.ScratchableLinearLayout
android:id="#+id/scratch_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#color/gold" >
<ImageView
android:layout_width="73dp"
android:layout_height="42dp"
android:src="#drawable/scratch_here_ic" />
</com.jackpocket.scratchoff.views.ScratchableLinearLayout>
</RelativeLayout>
</FrameLayout>
And RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/scratchOffGameRecycle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/youWin"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:layout_marginRight="50dp"
android:layout_marginBottom="10dp"
android:background="#drawable/bg_gold_rectangle"
android:columnWidth="100dp"
android:numColumns="3" />
As additional information, the libraries that I've tried using are these: 'com.jackpocket:scratchoff:1.3.0', 'com.github.cooltechworks:ScratchView:v1.1'in conjuction with both recycler and grid views to no avail.
What I ended up doing was changing the scratchoff library to this one: 'com.goibibo.libs:scratchcardview:0.1.6' ('com.jackpocket:scratchoff:1.3.0' had problems setting the listener inside the OnBindViewHolder method) and making a compromise to only save fully scratched views and not partially scratched ones. Here are the modifications I made:
OnBindViewHolder:
override fun onBindViewHolder(#NonNull holder: ViewHolder, position: Int) {
holder.itemView.number.text = mData[position].number
var scratchRelativeLayoutView = holder.itemView.scratch_card
holder.setIsRecyclable(false)
if(mData[position].isScratched && scratchRelativeLayoutView!=null) {
scratchRelativeLayoutView.visibility = View.GONE
holder.itemView.numberHidden.text = mData[position].number
holder.itemView.numberHidden.visibility = View.VISIBLE
}
else {
scratchRelativeLayoutView.setStrokeWidth(5)
scratchRelativeLayoutView.setScratchView(R.layout.lyt_scratch) // scratchable layout
scratchRelativeLayoutView.setRevealListener(object : ScratchRelativeLayoutView.IRevealListener {
override fun onRevealed(tv: ScratchRelativeLayoutView) {
mData[position].isScratched = true
}
override fun onRevealPercentChangedListener(siv: ScratchRelativeLayoutView, percent: Float) {
if (percent>0.5) {
siv.reveal()
}
}
})
}
}
scratch_off_game_item.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/achievementItem"
android:layout_width="90dp"
android:layout_height="90dp">
<TextView
android:id="#+id/numberHidden"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fontFamily="#font/zing_rust_demo_base"
android:gravity="center"
android:text="23"
android:background="#color/white_scratch_off"
android:textColor="#color/black"
android:textSize="24sp"
android:layout_gravity="center"/>
<com.goibibo.libs.views.ScratchRelativeLayoutView
android:id="#+id/scratch_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white_scratch_off">
<TextView
android:id="#+id/number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/zing_rust_demo_base"
android:gravity="center"
android:textColor="#color/black"
android:textSize="24sp"
android:layout_centerInParent="true"/>
</com.goibibo.libs.views.ScratchRelativeLayoutView>
</FrameLayout>