0

I'm new to Kotlin and Android development in general. I have created a custom adapter that extends BaseAdapter and it works as I want it to, but as soon as I was to add a click listener to the view that gets returned in the getView, it doesn't seem to work as expected.

I have looked everywhere for an answer. I have seen people talking about clickable, duplicateParentState, focusable,... But nothing seems to work

The click listener does react if I click on the left/right border of my layout RelativeLayout, but I want the entire RelativeLayout to react when I click.

Am I missing something in my XML? Or is there some kind of "hack" that works. I'm a little confused as to why my click listener is not working as expected

My TokenAdapter (extends BaseAdapter)

class TokenAdapter(private val ctx: Context) : BaseAdapter() {
    private val tokenPersistence: TokenPersistence = TokenPersistence(ctx)
    private val clipboardManager: ClipboardManager =
        ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var seconds: Int
    var percentage: Int = 100
    var shouldGenerateToken: Boolean = true

    init {
        seconds = getSecondsUntilRefresh()
    }

    fun getSecondsUntilRefresh() : Int{
        val secondsElapsedInMinute = LocalDateTime.now().second
        return if(secondsElapsedInMinute < 30) 30 - secondsElapsedInMinute else 60 - secondsElapsedInMinute
    }

    override fun getCount(): Int {
        return tokenPersistence.length()
    }

    override fun getItem(position: Int): Token? {
        return tokenPersistence.get(position)
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val v: View = if (convertView == null) {
            val inflater: LayoutInflater =
                    ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            inflater.inflate(R.layout.list_item, parent, false)
        } else {
            convertView
        }

        val t: Token = getItem(position)!!

        val title: TextView = v.findViewById(R.id.list_item_title)
        val code: TextView = v.findViewById(R.id.list_item_subtitle)
        title.text = t.getLabel()
        if (shouldGenerateToken) {
            var generatedCode : String = t.generateCode()
            generatedCode = generatedCode.substring(0, 3) + " " + generatedCode.substring(3, generatedCode.length)
            code.text = generatedCode
            shouldGenerateToken = false
        }

        val countdown: ProgressBar = v.findViewById(R.id.progress_circular)
        val countdownText: TextView = v.findViewById(R.id.progress_circular_text)

        countdownText.text = seconds.toString()
        countdown.progress = percentage

        v.setOnClickListener {
            val copyText = code.text.split(' ').joinToString("")
            println("===================")
            println("Copied: $copyText")

            val clip: ClipData = ClipData.newPlainText("2FA Code", copyText)
            clipboardManager.setPrimaryClip(clip)

            Toast.makeText(ctx, "Copied: $copyText to clipboard", Toast.LENGTH_LONG).show()
        }

        return v
    }
}

The list_item XML layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/list_item_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/list_item_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:textSize="16sp"
        android:textStyle="bold"
        android:paddingHorizontal="8dp"
        android:paddingTop="16dp"
        android:paddingBottom="0dp"
        android:clickable="false"
        />


    <TextView
        android:id="@+id/list_item_subtitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_below="@id/list_item_title"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_toStartOf="@id/progress_circular_container"
        android:layout_toLeftOf="@id/progress_circular_container"
        android:paddingHorizontal="8dp"
        android:paddingTop="0dp"
        android:text="123456"
        android:textColor="@color/primary"
        android:textSize="32sp"
        android:textStyle="bold"
        android:clickable="false"/>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/progress_circular_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_below="@id/list_item_title"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:clickable="false">
            <ProgressBar
                android:id="@+id/progress_circular"
                android:layout_width="48sp"
                android:layout_height="48sp"
                android:indeterminateOnly="false"
                android:progressDrawable="@drawable/pb_circular_determinative"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:progress="100"/>

            <TextView
                android:id="@+id/progress_circular_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/black"
                app:layout_constraintBottom_toBottomOf="@+id/progress_circular"
                app:layout_constraintEnd_toEndOf="@+id/progress_circular"
                app:layout_constraintStart_toStartOf="@+id/progress_circular"
                app:layout_constraintTop_toTopOf="@+id/progress_circular"
                tools:text="30" />
        </androidx.constraintlayout.widget.ConstraintLayout>

</RelativeLayout>
Kevin Galligan
  • 16,159
  • 5
  • 42
  • 62
Flame
  • 33
  • 6
  • And what if instead of setting the listener on the view, you set it on the relative layout: v.findViewById(R.id.list_item_container).setOnClickListener {...} – Nestor Perez Jul 12 '21 at 21:00
  • Everything looks OK and it should work as you expect, i.e., you should see the click event wherever you click within the layout. Are you sure that you are not manipulating the view or the click handler somewhere else? Also, are you certain the click handler is not being invoked? – Cheticamp Jul 12 '21 at 23:02
  • @NestorPerez I have tried that, but it acts the exact same as the view does. – Flame Jul 13 '21 at 05:57
  • @Cheticamp Well it is being invoked, but only on the right/left border of my RelativeLayout, so it seems like it is being blocked by some child. Although I'm clearly setting my childs to clickable="false" so in theory it should force all click events to go through my listener – Flame Jul 13 '21 at 05:57
  • Is there maybe a way to catch all click events on screen and check what View is triggering them? That way I could narrow down my search area – Flame Jul 13 '21 at 06:03
  • You can start with setting a break point in `dispatchTouchEvent()` in the activity and follow the chain down. See [this post](https://stackoverflow.com/questions/7449799/how-are-android-touch-events-delivered/46862320#46862320). – Cheticamp Jul 13 '21 at 12:30
  • I have managed to solve it, not sure what exactly did solve it. But I think it is because I moved my clicklistener out of my getView and into my MainActivity, although it could also be because I changed my list_item.xml. I will be posting my new code to potentially help people in the future – Flame Jul 13 '21 at 13:36

1 Answers1

1

So I managed to fix my problem.

I did 2 "major" things after which my problem was resolved:

  1. I rewrote my list_item.xml making use of LinearLayout instead of RelativeLayout (although I doubt this did anything)

     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:orientation="vertical">
    
         <TextView
                 android:id="@+id/list_item_title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textColor="#000000"
                 android:textSize="16sp"
                 android:textStyle="bold"
                 android:paddingHorizontal="8dp"
                 android:paddingTop="16dp"
                 android:paddingBottom="0dp"
                 />
    
    
             <TextView
                 android:id="@+id/list_item_subtitle"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:paddingHorizontal="8dp"
                 android:paddingTop="0dp"
                 android:textColor="@color/primary"
                 android:textSize="32sp"
                 android:textStyle="bold"
                 />
     </LinearLayout>
    
    
     <androidx.constraintlayout.widget.ConstraintLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center">
                 <ProgressBar
                     android:id="@+id/progress_circular"
                     android:layout_width="48sp"
                     android:layout_height="48sp"
                     android:indeterminateOnly="false"
                     android:progressDrawable="@drawable/pb_circular_determinative"
                     app:layout_constraintBottom_toBottomOf="parent"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintStart_toStartOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:progress="100"/>
    
                 <TextView
                     android:id="@+id/progress_circular_text"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:textColor="@android:color/black"
                     app:layout_constraintBottom_toBottomOf="@+id/progress_circular"
                     app:layout_constraintEnd_toEndOf="@+id/progress_circular"
                     app:layout_constraintStart_toStartOf="@+id/progress_circular"
                     app:layout_constraintTop_toTopOf="@+id/progress_circular"
                     tools:text="30" />
             </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. I moved my ClickListener from my Adapter getView() to my MainActivity with an setOnItemClickListener on my ListView

         val listview = findViewById<ListView>(R.id.tokenList)
    
         listview.setOnItemClickListener { parent: AdapterView<*>, view: View, position: Int, id ->
             val token = tokenAdapter.getItem(position)
    
             if(token != null) {
                 val code = token.generateCode()
                 val clip: ClipData = ClipData.newPlainText("2FA Code", code)
                 clipboardManager.setPrimaryClip(clip)
                 Toast.makeText(this, "Copied: $code", Toast.LENGTH_LONG).show()
             }
         }
    

Using this method on my Listview I also didn't have to use clickable, or any of those kinds of properties on my XML layouts. If someone knows what exactly made it work, please tell me. Personally I think it is the Listener being an 'OnItemClick' and it being moved to the MainActivity, although don't take my word for it as it could just be something else random that I did.

Flame
  • 33
  • 6