Search code examples
androidkotlinadapterrecyclerview-layout

How can I prevent losing a ScratchView's state when using it together with an adapter?


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.


Solution

  • 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>