In my app's preferences I have an option to change app's language.
public class Fragment_Preferences extends PreferenceFragment {
private SharedPreferences.OnSharedPreferenceChangeListener prefListener;
SharedPreferences preferences;
#Override
public void onCreate(Bundle savedInstanceState) {
preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if(key.equals("language_preference"))
{
// Set language change flag to true -
// the Main Fragment will be recreate when this fragment finishes and the main restarts
Common_Methods.set_locale_changed(true);
}
}
};
prefs.registerOnSharedPreferenceChangeListener(prefListener);
}
}
So when I change the language, the preference fragment doesn't change it's language immediately. I have to exit preferences, then in my Fragment or Activity (depends from where I called for Preference fragment) I have this code, which restarts the current fragment or activity with new language settings:
public void onConfigurationChanged(Configuration newConfig) {
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
#Override
public void run()
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
{
finish();
startActivity(getIntent());
} else {recreate();}
}
}, 1);
Common_Methods.set_locale_changed(false); // Reset the Language change flag to prevent repeating Fragment recreation.
super.onConfigurationChanged(newConfig);
}
If I reopen Preference fragment at that point - it will be in the new selected language.
I tried to copy the above method to my Preference_Fragment, but I'm getting errors. So the question is: how can I recreate/reload the Preference_Fragment with the new language immediately after it was selected, without having to exit the fragment first?
It is best if you let the activity know (through getActivity() and casting) that you want your fragment updated, and add logic to the activity to remove the fragment and add a new instance.
In your settings activity:
public void restartFragment() {
SettingsFragment fragment = new SettingsFragment();
getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
}
And in your settings fragment:
((SettingsActivity) getActivity()).restartFragment();
Floren's answer didn't work for me (maybe due to using the Android Support Library), but it lead me to the following solution based on the same basic idea from Florens. The difference is removing and adding the fragment instead of replacing:
In your activity:
public void restartFragment() {
FragmentManager fm = getSupportFragmentManager();
if (fragment != null) {
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fragment);
ft.commit();
}
fragment = <new instance>;
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragmentActivity, main, "Main");
ft.commit();
}
In your fragment:
((<your activity>)getActivity()).restartFragment();
Related
I have an Android app that has used Preferences UI. I want to be able to deep link into the specific preference option. I can't figure out how to do that.
I have set up the preferences using the tutorial as follows
https://developer.android.com/guide/topics/ui/settings
https://medium.com/#JakobUlbrich/building-a-settings-screen-for-android-part-1-5959aa49337c (Part 1)
https://medium.com/#JakobUlbrich/building-a-settings-screen-for-android-part-2-2ba63e2d7d1d
https://medium.com/#JakobUlbrich/building-a-settings-screen-for-android-part-3-ae9793fd31ec
I have set up deep linking activity using this tutorial
https://developer.android.com/training/app-links/deep-linking
I want to be able to deep link directly into the one of the preferences.
public class SettingsActivity extends AppCompatActivity
implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
FragmentFactory fragmentFactory = new AppFragmentFactory();
final FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager != null)
{
if (fragmentManager.findFragmentByTag(ROOT_FRAGMENT_TAG) == null)
{
final int containerId = R.id.detail_container;
final Fragment fragment = fragmentFactory.create(RootPreferencesFragment.class);
fragment.setArguments(savedInstanceState);
fragmentManager.beginTransaction()
.add(containerId, fragment, ROOT_FRAGMENT_TAG)
.commit();
}
}
}
}
#Override
public boolean onPreferenceStartFragment(PreferenceFragmentCompat callingFragment, Preference preference)
{
final Bundle arguments = preference.getExtras();
final Fragment fragment = fragmentFactory.create(getClassLoader(), preference.getFragment());
fragment.setArguments(arguments);
fragment.setTargetFragment(callingFragment, 0);
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.setBreadCrumbTitle(preference.getTitle())
.addToBackStack(preference.getKey())
.commit();
return true;
}
RootPreferencesFragment
public class RootPreferencesFragment extends PreferenceFragmentCompat implements DialogFragmentCallback
{
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
final FragmentActivity activity = Objects.requireNonNull(getActivity());
// Notifications
final Preference notificationsPreference = preferencesFactory.create(
accountCategory,
Preference.class,
RootViewModel.ACCOUNT_NOTIFICATIONS_PREFERENCE_KEY,
R.string.PushNotifications,
RootViewModel.ACCOUNT_CATEGORY_KEY);
notificationsPreference.setFragment(NotificationPreferencesFragment.class.getName());
// General Category
final PreferenceCategory generalCategory = preferencesFactory.create(
preferenceScreen,
PreferenceCategory.class,
RootViewModel.GENERAL_CATEGORY_KEY,
R.string.preferences_general_group);
// About category
final PreferenceCategory aboutCategory = preferencesFactory.create(
preferenceScreen,
PreferenceCategory.class,
RootViewModel.ABOUT_CATEGORY_KEY,
R.string.preferences_about_group);
// About
final Preference aboutPreference = preferencesFactory.create(
aboutCategory,
Preference.class,
RootViewModel.ABOUT_PREFERENCE_KEY,
R.string.preferences_about);
aboutPreference.setSummary(aboutViewModel.getAppVersion());
aboutPreference.setFragment(AboutPreferencesFragment.class.getName());
}
}
I don't know how to trigger deep link into a specific NotificationsPreference. There is only one activity, but there are three preferences.
Imagine one activity with 3 fragments: starts showing the first one, select a menu option and go to the second one, select another option and go to the 3rd fragment and select again the first option an return to the second one.
f1 -> f2 -> f3 -> f2
When I press back I want the app returns to fragment 3 and when I press back again it should return to fragment 1 and if press back again, close the app.
Something like if the fragment exists, move it to top of the stack and if not, create it.
Thank you!
Here is solution I came up over time.
The idea is following, you need to keep a stack data structure and whenever you add a fragment add it to stack as well, then override onBackPress method and check if stack is not empty then replace your fragment container with new fragment from top of the stack when it is empty do super.onbackpress
So here is a parent class for all kind of fragment based navigation.
public abstract class FragmentsStackActivity extends BaseActivity {
public static final String TAG_BUNDLE = "bundle_tag";
protected final Bundle fragmentArgs = new Bundle();
protected Stack<Fragment> fragments = new Stack<>();
abstract protected void setupFragments();
public void setFragmentArguments(Fragment fragment, Bundle arguments){
if(!fragments.isEmpty() && fragments.peek()!=fragment){
fragment.setArguments(arguments);
}
}
public void setFragmentFromStack() {
if(!fragments.isEmpty()) {
Fragment fragment = fragments.peek();
final Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (oldFragment == null || oldFragment != fragment) {
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//transaction.setCustomAnimations(R.anim.animator_left_right_in, R.anim.animator_left_right_in);
transaction.replace(R.id.fragment_container, fragment).commit();
}
}else {
finish();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//TODO need to save fragment stack
}
}
example of an activity that extends this class
public class LoginActivity extends FragmentsStackActivity{
private final MyFragment1 fragment1 = new MyFragment1();
private final MyFragment2 fragment2 = new MyFragment2();
private final User mUser = new User();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setupFragments();
setFragmentFromStack();
}
#Override
protected void setupFragments() {
fragments.add(fragment2);
//fragment2.setNotifier(this); // I use notifiers listener but you can choose whatever convenient for you
Bundle fragmentArgs = new Bundle();
fragmentArgs.putBoolean(Constants.TAG_LOGIN, true);
fragmentArgs.putParcelable(User.TAG, mUser);
fragmentArgs.putInt(Constants.TYPE, getIntent().getIntExtra(Constants.TYPE, 0));
fragment2.setArguments(fragmentArgs);
//fragment1.setNotifier(this); // I use notifiers listener but you can choose whatever convenient for you
}
// this method teals with handling messages from fragments in order to provide navigation
// when some actions taken inside the fragment, you can implement your own version
public void onReceiveMessage(String tag, Bundle bundle) {
switch (tag) {
case MyFragment2.TAG_BACK:
case MyFragment1.TAG_BACK:
fragments.pop();
setFragmentFromStack();
break;
case MyFragment2.TAG_NEXT:
fragment1.setArguments(bundle);
fragments.add(fragment1);
setFragmentFromStack();
break;
case MyFragment1.TAG_NEXT:
goToWelcomeScreen(bundle);
finish();
break;
}
}
private void goToWelcomeScreen(Bundle bundle){
}
}
You can implement this with the help of the following code:
// transaction.replace(R.id.detailFragment, frag1);
Transaction.remove(null).add(frag1) // frag1 on view
// transaction.replace(R.id.detailFragment, frag2).addToBackStack(null);
Transaction.remove(frag1).add(frag2).addToBackStack(null) // frag2 on view
// transaction.replace(R.id.detailFragment, frag3);
Transaction.remove(frag2).add(frag3) // frag3 on view
And for better understanding, have a ook at the following snippet:
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
have a look here http://developer.android.com/training/implementing-navigation/temporal.html
I created PreferenceActivity with button Preference out and appointed her event.
I need to clear the username and password (I just clear out the Preference) and go to the fragment "authorization". My fragments extends from
android.support.v4.app.Fragment;
android.support.v4.app.FragmentManager;
I can not I get getFragmentManager(); (android.support.v4.app.FragmentManager) from PreferenceActivity
public class PrefActivity extends PreferenceActivity {
Preference out;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref);
out = findPreference("logout");
out.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
FragmentManager fragmentManager = getFragmentManager();
Fragment auth = new AuthDialog();
Bundle arg = new Bundle();
arg.putString("login", "");
arg.putString("password", "");
auth.setArguments(arg);
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.content_frame, auth).commit();
SharedPreferences sPref = PreferenceManager.getDefaultSharedPreferences(getApplication());
sPref.edit().clear().commit();
return false;
}
});
}
}
PreferenceActivity extends from android.app.Activity and not android.support.v4.app.Activity. Strange as it may sound, there is no way to use v4 Fragments with PreferenceActivity. Your best bet is to use this library
i'm developing an application where I have tabs and fragments within each tab. I got it to work where it shows my listview on launch and when I click a name in the list its goes to the next fragment, but when I hit the back button it doesn't go back to the listview I just get a blank screen. I'm sure it's a simple answer and I have posted some code snippets, if more is needed please let me know. Thank you for your help.
FragmentActivity
public class HomeStackActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmentstackctivity);
// add initial fragment, do not add to back stack, no transition animation
addFragment(new MyCommentActivity(), false, FragmentTransaction.TRANSIT_NONE);
}
void addFragment(Fragment fragment, boolean addToBackStack, int transition) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment1, fragment);
ft.setTransition(transition);
if (addToBackStack)
ft.addToBackStack(null);
ft.commit();
}
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0)
getSupportFragmentManager().popBackStack();
else
finish();
}
}
NameClick
public class nameOnClickListener implements OnClickListener{
private int position;
public nameOnClickListener(int position){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = commentList.get(position);
Log.d("Testing Click", update.get(MyCommentActivity.KEY_UID));
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor edit = sp.edit();
edit.putString("profile_id", update.get(MyCommentActivity.KEY_UID));
edit.putString("profile_type", update.get(MyCommentActivity.KEY_TYPE));
edit.commit();
addFragment(new UserProfileActivity(), true, FragmentTransaction.TRANS
change:
ft.replace(R.id.fragment1, fragment);
to:
ft.add(R.id.fragment1, fragment);
I am useing This Sort of Code To Handle Three Fragment in Main Activity ...
FragmentA is Fixed it One Frame .. I change FragmentB and FragmentC on Button Click on FragmentA.
his Code is Running well either in Portrait or Landscape view .Here is The Code bellow.
public class MainActivity extends FragmentActivity implements
OnSwitchClickListener {
FragmentManager manager;
FragmentA fragA;
FragmentB fragB;
FragmentC fragC;
boolean fragBSet = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragA = new FragmentA();
fragB = new FragmentB();
fragC = new FragmentC();
manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.add(R.id.a_container, fragA, "frag A");
if (fragBSet) {
ft.add(R.id.bc_container, fragC, "frag C");
} else {
ft.add(R.id.bc_container, fragB, "frag B");
}
fragBSet = true;
ft.commit();
}
#Override
public void onSwitchClick(View v) {
Toast.makeText(getApplicationContext(), "Switch clkick from Activity",
Toast.LENGTH_LONG).show();
FragmentTransaction ft = manager.beginTransaction();
if (fragBSet) {
ft.remove(fragB);
ft.add(R.id.bc_container, fragC, "frag C");
fragBSet = false;
} else {
ft.remove(fragC);
ft.add(R.id.bc_container, fragB, "frag B");
fragBSet = true;
}
ft.commit();
}
This Code is Running well either in Portrait or Landscape view ... But When Ever I change the Orientation The Two Fragments Override One Another.
Need Solution.
When your orientation change, your Activity is recreated.
"In the scenario where a user rotates their device, Android will destroy your
application’s activity(s). Before destroying them it calls, onSaveInstanceState,
allowing developers to persist data. Once the activity is recreated post
rotation, the OS will call onRestoreInstanceState giving developers a chance to
restore the application state pre-rotation."
You should try to save your Fragments State.
putFragment: Put a reference to a fragment in a Bundle. This Bundle can be persisted as saved state, and when later restoring getFragment(Bundle, String) will return the current instance of the same fragment.
#Override
protected void onSaveInstanceState(Bundle outState) {
FragmentManager manager = getFragmentManager();
manager.putFragment(outState, MyFragment.TAG, mMyFragment);
}
getFragment: Retrieve the current Fragment instance for a reference previously placed with putFragment(Bundle, String, Fragment).
private void instantiateFragments(Bundle inState) {
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (inState != null) {
mMyFragment = (MyFragment) manager.getFragment(inState, MyFragment.TAG);
} else {
mMyFragment = new MyFragment();
transaction.add(R.id.fragment, mMyFragment, MyFragment.TAG);
transaction.commit();
}
}
restoreFragment:
#Override
protected void onRestoreInstanceState(Bundle inState) {
instantiateFragments(inState);
}
Hope this helps.