I have 2 fragments :
HomeFragment
that extends Fragment
SettingsFragment
that extends PreferenceFragment
In the MainActivity
, I have 2 buttons :
SettingsFragment
(denoted summary)
The summary in SettingsFragment
is a TextView
's text inside a CustomPreference
used in SettingsFragment
.I want to update summary inside CustomPreference
using the updateButton.
The first time SettingsFragment
is added to the activity, the updateButton fails to set the summary. However, when I use the switchButton to put back the SettingsFragment
, it works fine.
I have put logs at the beginning of every function hoping to track down the problem.
The first time SettingsFragment
is put and updateButton is clicked, I get the following log
D/MainActivity: onCreate
D/MainActivity: Fragments button clicked
D/SettingsFragment: onCreate
D/CustomPreference: CustomPreference constructor
D/SettingsFragment: onViewCreated
D/CustomPreference: set summary : Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/MainActivity: Update button clicked
D/SettingsFragment: setSummary : Hello Preference (0)
D/CustomPreference: set summary : Hello Preference (0)
setting mSummaryTextView to Hello Preference (0)
Now, if I put back the SettingsFragment
for the second time and click the updateButton, I get the following log
D/MainActivity: Fragments button clicked
D/SettingsFragment: onCreate
D/CustomPreference: CustomPreference constructor
D/SettingsFragment: onViewCreated
D/CustomPreference: set summary : Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/CustomPreference: onBindView
setting mSummaryTextView to Started
D/MainActivity: Update button clicked
D/SettingsFragment: setSummary : Hello Preference (1)
D/CustomPreference: set summary : Hello Preference (1)
setting mSummaryTextView to Hello Preference (1)
(notice that onBindView
is called once more for the second case)
Below, I put my code
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
HomeFragment homeFragment = new HomeFragment();
SettingsFragment settingsFragment = new SettingsFragment();
int counter = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set settingsFragment as the default fragment
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, homeFragment).commit();
// A simple button to switch between the two fragments
Button switchButton = findViewById(R.id.switchButton);
switchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Fragments button clicked");
if(homeFragment.isAdded()) {
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, settingsFragment).commit();
} else {
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, homeFragment).commit();
}
}
});
// A simple button to change the summary of the preference
Button updateButton = findViewById(R.id.updateButton);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Update button clicked");
if(settingsFragment.isAdded()) {
settingsFragment.setSummary("Hello Preference (" + counter + ")");
counter++;
}
}
});
}
}
HomeFragment.java
public class HomeFragment extends Fragment {
private static final String TAG = "HomeFragment";
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 {
private static final String TAG = "SettingsFragment";
CustomPreference customPreference;
String mSummary;
public SettingsFragment() {
// Required empty public constructor
}
public void setSummary(String text) {
Log.d(TAG, "setSummary : " + text);
((CustomPreference) findPreference("test_key")).setSummary(text);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_settings);
mSummary = "Started";
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onViewCreated");
super.onViewCreated(view, savedInstanceState);
customPreference = (CustomPreference) findPreference("test_key");
customPreference.setSummary(mSummary);
}
}
CustomPreference.java
public class CustomPreference extends Preference {
private static final String TAG = "CustomPreference";
private String mSummary;
private TextView mSummaryTextView;
public CustomPreference(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "CustomPreference constructor");
// Handle customized attributes
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.CustomPreference, 0, 0);
mSummary = a.getString(R.styleable.CustomPreference_summary);
// TypedArray objects are shared and must be recycled
a.recycle();
}
public void setSummary(String summary) {
Log.d(TAG, "set summary : " + summary);
this.mSummary = summary;
if (mSummaryTextView != null) {
Log.d(TAG, "setting mSummaryTextView to " + mSummary);
mSummaryTextView.setText(mSummary);
mSummaryTextView.invalidate();
}
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Log.d(TAG, "onBindView");
mSummaryTextView = view.findViewById(R.id.summaryTextView);
if (mSummaryTextView != null) {
Log.d(TAG, "setting mSummaryTextView to " + mSummary);
mSummaryTextView.setText(mSummary);
}
}
}
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/updateButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/activityText"
android:text="Update"/>
<Button
android:id="@+id/switchButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/updateButton"
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/switchButton"
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"
xmlns:preference="http://schemas.android.com/apk/res-auto">
<com.gosense.myapplication.CustomPreference
android:key="test_key"
preference:summary="My custom preference"
android:layout="@layout/preference_layout"/>
</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/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAllCaps="true"
android:textColor="@color/colorAccent"
android:text="Title"/>
<TextView
android:id="@+id/summaryTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/titleTextView"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAllCaps="true" />
</android.support.constraint.ConstraintLayout>
I found the solution
In fact, inside the CustomPreference
, each time we do changes to inner views, we must notify about these changes using notifyChanged();
. Therefore, when I changed the line mSummaryTextView.invalidate();
in CustomPreference.java by notifyChanged();
, it worked straight away.
Hope that helps