android – Fragment's view not getting focus after replacing using Talkback-ThrowExceptions

Exception or error:

When using Talkback the replaced view produces a weird result in the top fragment. The button that triggers the transaction of the fragment is still visible(this also happens without the accessibility service on) and it still has the focus. The expected result is to get the accessibility focus in the fragment’s TextView after the transaction is commited and not showing the button from replaced root view. I left the commented lines so you can see what I have tried and did not work but might help others. Notice I used both kotlin and java so that the fix can be implemented in any of those.

The code:

FragmentDetail.java

import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.TextView;
import com.hztlscroll.R;
import com.hztlscroll.ext.FragmentManagerExtensionKt;

public class FragmentDetail extends Fragment {
    private FragmentManager fm = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
        fm = getFragmentManager();
//        FragmentManagerExtensionKt.setupForAccessibility(fm);
        View view = inflater.inflate(R.layout.fr_detail, viewGroup, false);
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//            view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
//        }
        TextView tvLbl01 = (TextView) view.findViewById(R.id.tv_detail_title);
        tvLbl01.setText(R.string.detail_fr_title);
        tvLbl01.requestFocus();
//        tvLbl01.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//            tvLbl01.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
//        }
        return view;
    }
}

fr_detail.xml

<?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_gravity="center"
    android:background="@color/colorAccent"
    android:orientation="vertical"
    android:focusable="true"
    android:clickable="true"
    android:importantForAccessibility="yes">

    <TextView
        android:id="@+id/tv_detail_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/landing_fr_msg01_lbl"
        android:textColor="#b0b0ff"
        android:textSize="20sp" />
</LinearLayout>

MainActivity.kt

import android.app.Fragment
import android.app.FragmentManager
import android.app.FragmentTransaction
import android.os.Build
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import com.hztlscroll.ext.setupForAccessibility
import com.hztlscroll.ui.FragmentDetail


class MainActivity : AppCompatActivity() {
    var fm: FragmentManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fm = fragmentManager
//        fm?.setupForAccessibility()
        setContentView(R.layout.activity_main)
        val landingRootView = findViewById<ConstraintLayout>(R.id.cl_landing)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//            landingRootView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
//        }
        val landingBtn: Button = findViewById(R.id.btn_landing_01)
        landingBtn.setOnClickListener(listener)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return super.onTouchEvent(event)
    }

    val listener: View.OnClickListener = object : View.OnClickListener {
        override fun onClick(view: View?) {
            var fragment: Fragment? = null
            if (view === this@MainActivity.findViewById<Button>(R.id.btn_landing_01)) {
                fragment = FragmentDetail()
            }
            val ft: FragmentTransaction = fm!!.beginTransaction()
            ft.replace(R.id.cl_landing, fragment);
            ft.commit()
        }
    }
}

activity_main.xml

<?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/cl_landing"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_landing_01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/tv_landing_act_focus"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tv_landing_act_focus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/fl_landing_focus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/et_landing_act_focus">

        <ImageView
            android:layout_width="250px"
            android:layout_height="250px"
            android:scaleType="fitCenter"
            android:src="@drawable/ic_launcher_background" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:text="Frame Demo"
            android:textSize="30px"
            android:textStyle="bold" />
    </FrameLayout>

    <EditText
        android:id="@+id/et_landing_act_focus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_landing_act_focus" />
</androidx.constraintlayout.widget.ConstraintLayout>

FragmentManagerExtension.kt

import android.app.FragmentManager
import android.os.Build
import android.view.View

    fun FragmentManager.setupForAccessibility() {
        addOnBackStackChangedListener {
            val lastFragmentWithView = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                fragments.last { it.view != null }
            } else {
                TODO("VERSION.SDK_INT < O")
            }
            for (fragment in fragments) {
                if (fragment == lastFragmentWithView) {
                    fragment.view?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
                } else {
                    fragment.view?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                }
            }
        }
    }

And the consulted sources so far:

post 1

post 2

Image of the screen after the transaction having focus and the button visible instead of the fragment’s title..
fragment’s TextView not getting focus

How to solve:

Leave a Reply

Your email address will not be published. Required fields are marked *