Search code examples
androidandroid-fragmentsfragmentpreferencepreferencefragment

findViewById fails in onViewCreated of a PreferenceFragment, Android


I have two fragments :
- HomeFragment that extends Fragment
- SettingsFragment that extends PreferenceFragment

Each one contains a TextView that I wish to access and modify its content in the Java code. I make this modification inside the onViewCreated by calling findViewById and then, using setText on the TextView.
This works fine for HomeFragment, but it does not in SettingsFragment.
For information, the TextView of SettingsFragment is defined in the layout associated to the Preference. Strangely, it seems that the views hierarchy of PreferenceFragment type is not fully ready in the onViewCreated ! To go further, I tried to put the code that modifies the TextView after some delay (using postDelay), and that worked !

Below, I put all the code necessary to test the issue I have just raised.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final HomeFragment homeFragment = new HomeFragment();
        final SettingsFragment settingsFragment = new SettingsFragment();

        getFragmentManager().beginTransaction()
                .add(R.id.fragmentContainer, homeFragment).commit();

        // Just a simple button to switch between the two fragments
        Button button = findViewById(R.id.activityButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(homeFragment.isAdded()) {
                    getFragmentManager().beginTransaction()
                            .replace(R.id.fragmentContainer, settingsFragment).commit();
                } else {
                    getFragmentManager().beginTransaction()
                            .replace(R.id.fragmentContainer, homeFragment).commit();
                }
            }
        });
    }
}

HomeFragment.java

public class HomeFragment extends Fragment {

    public HomeFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        TextView textView = view.findViewById(R.id.homeText);
        if(textView != null) {
            textView.setText("Home fragment text (modified)");
        }
    }
}

SettingsFragment.java

public class SettingsFragment extends PreferenceFragment {


    public SettingsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.fragment_settings);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // ---- BLOCK 1 -----
        TextView textView = view.findViewById(R.id.settingsText);
        if (textView != null) {
            textView.setText("Preference 1 (modified)");
        }

        // ---- BLOCK 2 -----
//        view.post(new Runnable() {
//            @Override
//            public void run() {
//                TextView textView = view.findViewById(R.id.settingsText);
//                if (textView != null) {
//                    textView.setText("Preference 1 (modified)");
//                }
//            }
//        });

        // ---- BLOCK 3 -----
//        view.postDelayed(new Runnable() {
//            @Override
//            public void run() {
//                TextView textView = view.findViewById(R.id.settingsText);
//                if (textView != null) {
//                    textView.setText("Preference 1 (modified)");
//                }
//            }
//        }, 100);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".MainActivity">

    <TextView
        android:id="@+id/activityText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:gravity="center_horizontal"
        android:text="MainActivity" />

    <Button
        android:id="@+id/activityButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/activityText"
        android:text="Change fragment"/>

    <FrameLayout
        android:id="@+id/fragmentContainer"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/activityButton"
        app:layout_constraintBottom_toBottomOf="parent">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".HomeFragment">

    <TextView
        android:id="@+id/homeText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Home fragment text" />

</android.support.constraint.ConstraintLayout>

fragment_settings.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:layout="@layout/preference_layout_category">

        <Preference
            android:enabled="true"
            android:layout="@layout/preference_layout" />

    </PreferenceCategory>
</PreferenceScreen>

preference_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/settingsText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textAllCaps="true"
        android:text="Preference 1"/>

</android.support.constraint.ConstraintLayout>

preference_layout_category.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/settingsTextContainer"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textAllCaps="true"
        android:background="@android:color/background_dark"
        android:textColor="@android:color/background_light"
        android:text="Category 1"/>

</android.support.constraint.ConstraintLayout>

Solution

  • I had something similar the other day. Use findViewById() in the on onCreateView of the fragment instead of the onViewCreated method. Good luck.