Skip to content

MoodToggle

ZieIony edited this page Apr 11, 2020 · 3 revisions

Making simple, custom views work with Talkback is very easy. I created a basic mood toggle that allows the user to change the selected mood by clicking. Nothing fancy.

inline class Mood(val image: Drawable)

class MoodToggle @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    var currentMood: Mood
        private set

    init {
        moods.add(Mood(resources.getDrawable(R.drawable.ic_sentiment_satisfied_black_24dp)))
        moods.add(Mood(resources.getDrawable(R.drawable.ic_sentiment_neutral_black_24dp)))
        moods.add(Mood(resources.getDrawable(R.drawable.ic_sentiment_dissatisfied_black_24dp)))
        currentMood = moods[0]
        isClickable = true
    }

    override fun performClick(): Boolean {
        super.performClick()
        currentMood = moods[(moods.indexOf(currentMood) + 1) % moods.size]
        invalidate()
        return true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        currentMood.image.setBounds(0, 0, width, height)
        currentMood.image.draw(canvas)
    }
}

This is the entire view class (and the Mood class). It just loads three images and changes them on click. I would like this class to provide voice feedback that it can be clicked and in what state it is. Making the node clickable causes Talkback to say "[content description], double tap to activate". It already correctly initializes the accessibility node and handles accessibility clicks by delegating to performClick(). Read more about events here.

There's one value that I needed to provide to AccessibilityNodeInfo - class name:

override fun getAccessibilityClassName() = MoodToggle::class.java.simpleName

Then I added content descriptions. I added the name field to the Mood class and a name for each mood. The content description is updated every time the selected mood changes.

data class Mood(val name: String, val image: Drawable)

init {
    moods.add(Mood("satisfied", resources.getDrawable(R.drawable.ic_sentiment_satisfied_black_24dp)))
    moods.add(Mood("neutral", resources.getDrawable(R.drawable.ic_sentiment_neutral_black_24dp)))
    moods.add(Mood("dissatisfied", resources.getDrawable(R.drawable.ic_sentiment_dissatisfied_black_24dp)))
    currentMood = moods[0]
    contentDescription = "mood ${currentMood.name}"
    isClickable = true
}

override fun performClick(): Boolean {
    currentMood = moods[(moods.indexOf(currentMood) + 1) % moods.size]
    contentDescription = "mood ${currentMood.name}"
    super.performClick()
    invalidate()
    return true
}

Calling setContentDescription(description) automatically refreshes the view and causes Talkback to read the new description. Calling super.performClick() additionally plays the click sound and sends an accessibility event telling the accessibility tool that the view was clicked.