I'm tryng study the Preference system of Android.
When from my navdrawer I call settings fragment, it doesn't show nothing and menu remain selected, also if I select another voice.
This is the screen at startup of my APP:
Where all is perfect. After I click on Impostazioni (settings, in English), Home (or the "Elenca" item) reamin checked, also with "Impostazioni"
Another one problem is that Settings frame is blank (no error in Log but it's all blank).
This is XML preferences.xml stored under res/xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/pref_sms_storage_title"
android:key="pref_key_storage_settings">
<CheckBoxPreference
android:key="pref_sync"
android:title="#string/pref_sync"
android:summary="#string/pref_sync_summ"
android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>
This is my Settings Fragment:
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.v4.app.Fragment;
import com.xxxxxx.R;
public class SettingsFragment extends Fragment{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
new InnerSettingsFragment();
}
public static class InnerSettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
}
}
And finally this is my BaseApp that mantains the navdrawer logic:
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
// This method will trigger on item Click of navigation menu
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
//Checking if the item is in checked state or not, if not make it in checked state
if (menuItem.isChecked()) menuItem.setChecked(false);
else menuItem.setChecked(true);
//Closing drawer on item click
drawerLayout.closeDrawers();
//Check to see which item was being clicked and perform appropriate action
switch (menuItem.getItemId()) {
case R.id.home:
DashboardFragment dashboardFragment = new DashboardFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, dashboardFragment,"DASHBOARD_FRAGMENT");
fragmentTransaction.commit();
return true;
case R.id.list_event:
ListEventFragment fragmentListEvent = new ListEventFragment();
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, fragmentListEvent);
fragmentTransaction.commit();
return true;
case R.id.settings:
SettingsFragment fragmentSettings = new SettingsFragment();
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, fragmentSettings);
fragmentTransaction.commit();
return true;
default:
return true;
}
}
});
You shouldn't make your PreferenceFragment an inner class, add your outer class in its place, and still expect things to work as usual. It won't because the outer onCreate() wouldn't delegate to the inner onCreate() by itself. So, just make your InnerSettingsFragment a regular top-level class and you'll start seeing your preferences get loaded from the XML resource.
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
}
Related
I have an activity, and a bunch of fragments in it. The fragments are not transitioned by sliding, but by button click. I'm trying to build a tab indicator like the one in the bottom that shows which fragment the user is. I don't even know how it's called.
This is my main Activity where all the fragments belong, and with the actionbar menu the user navigates throught the fragments. In the activity it's switched straight to the first fragment out of 4 fragments (DetailsFragment)
public class CreateWorkoutActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_workout);
DetailsFragment fragment = new DetailsFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.create_activity_frameLayout, fragment)
.addToBackStack(null)
.commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
}
return true;
}
return false;
}
image example
as i cannot see your code, I have this to offer https://guides.codepath.com/android/ViewPager-with-FragmentPagerAdapter ... refer this
In a Bluetooth-related app (with minSdkVersion="18") I have a single MainActivity.java, displaying one of the following 3 UI Fragments:
MainFragment.java (the top screen)
SettingsFragment.java (settings screen, entered through menu)
ScanningFragment.java (lists nearby Bluetooth devices)
To display an "Up button" and handle the "Back button" I have the following code in place:
public class MainActivity extends Activity
implements BleWrapperUiCallbacks {
// set in onResume() of each fragment
private Fragment mActiveFragment = null;
ยด #Override
public void onBackPressed() {
if (!getFragmentManager().popBackStackImmediate())
super.onBackPressed();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_ACTION_BAR);
setContentView(R.layout.activity_root);
getActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
Fragment fragment = new MainFragment();
getFragmentManager().beginTransaction()
.replace(R.id.root, fragment, "main")
.commit();
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getFragmentManager().popBackStackImmediate();
break;
case R.id.action_settings:
Fragment fragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.root, fragment, "settings")
.commit();
break;
}
return super.onOptionsItemSelected(item);
}
This works well, but has a cosmetic problem, that the "Up button" is still displayed when the MainFragment.java is being displayed - as you can see on the left side of the above screenshot.
I have tried calling
getActionBar().setHomeButtonEnabled(false);
when that fragment is being active, but that only disables the "Up button" - without really hiding it.
With help of Little Child (thanks!) here my solution using the FragmentManager.OnBackStackChangedListener:
public class MainActivity extends Activity
implements OnBackStackChangedListener,
BleWrapperUiCallbacks {
#Override
public void onCreate(Bundle savedInstanceState) {
...
getFragmentManager().addOnBackStackChangedListener(this);
}
#Override
public void onBackStackChanged() {
getActionBar().setDisplayHomeAsUpEnabled(
getFragmentManager().getBackStackEntryCount() > 0);
}
You are in luck because in API level 18 there is this:
getActionBar().setHomeAsUpIndicator(R.drawable.ic_yourindicator);
for support library, it is:
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_yourindicator);
So now depending upon what fragment you are in, you can change the icon of "up" button.
Also, try this:
getActionBar().setDisplayHomeAsUpEnabled(false);
Set whether home should be displayed as an "up" affordance.
2021 Working Solution:
(requireActivity() as AppCompatActivity).supportActionBar?.setHomeAsUpIndicator(null)
i'm trying to create a wizard Like Android application, i want to Create an activity and Two dynamic Fragments, the first one will be added when the Activity is created, and the second when the user clicks on a button in the First fragment, right now i can't even add the Fragment to the activity :
Activity onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment fr = new FragmentNumber();
getSupportFragmentManager().beginTransaction().add(fr, "number_fragment").commit();
}
this is my activity code, when i run this, the screen is blank.
the R.layout.activity_main refer to an empty Linear Layout, i don't want to add the fragments there because i need them to be dynamic.
Thanks in advance.
EDIT : pasting more files
activity_main.XML
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello from main"
/>
</FrameLayout>
MaicActivity.java
package com.example.fragmenttraining;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(findViewById(android.R.id.content) != null)
{
Log.d("main activity", "content found");
}
FragmentNumber fr = new FragmentNumber();
//getSupportFragmentManager().beginTransaction().add(android.R.id.content, fr, "number_fragment").commit();
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fr).commit();
}
FragmentNumber numberFragment;
FragmentFacebook facebookFragment;
public void facebookClicked(View view)
{
numberFragment = new FragmentNumber();
numberFragment.facebookClicked(view);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Now it's working, but the fragment is not replaced, it displays the content of the activity_main + the content of the FragmentNumber fragment even id i replace it ...
Follow these steps:
Create your main (activity layout) file. In it, add a frame layout which will act as a container for your fragments.
Now create your two fragments. This involves creating two xml files that will be inflated inside your fragment's onCreateView method.
One of your fragments (the first one) should have a button that the user will be able to click. That means you must attach an onClick listener to it inside the onCreateView method after finding it by id.
Now create an interface inside your first fragment and add a method in it that your activity should override after implementing the interface.
When the user clicks that button, inside onClick method, you should call the interface method to notify the activity of the click event.
Inside the activity, when the method is called, create a new instance of the second fragment and add it to view by replacing the first one - or it depends on whether you are using two-pane layout in your activity - in that case, you just add the fragment.
Remember to check if your fragment exists first before simply adding one to view.
I hope these steps help you.
Sample Code
public class WizardActivity extends Activity implements SecondFragment.OnButtonClickedListener
{
private FirstFragment firstFragment;
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
setContentView(R.layout.main);
firstFragment = new FirstFragment();
setFragment(firstFragment, "firstFragment");
}
#Override
public void loadSecondFragment()
{
SecondFragment secondFragment = new SecondFragment();
setFragment(secondFragment, "secondFragment");
}
public void setFragment(Fragment frag, String tag)
{
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragmentContainer);
if(fragment == null)
{
ft.add(R.id.fragmentContainer, frag, tag);
} else {
ft.replace(R.id.fragmentContainer, frag, tag);
}
ft.addToBackStack(null);
ft.commit()
}
}
Now the xml file for main layout.
<LinearLayout ........>
<!--add whatever you need here-->
<FrameLayout
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Now let us create one of your fragments - the first one:
FirstFragment.java
public class FirstFragment extends Fragment implements View.OnClickListener
{
private Activity mActivity;
#Override
public void onAttach(Activity act)
{
super.onAttach(act);
this.mActivity = act;
/*Initialize whatever you need here*/
}
#Override
public View onCreateView(LayoutInflator inflator, ViewGroup container, Bundle saveInstanceState)
{
View v = inflator.inflate(R.layout.first_fragment, container, false);
Button button = (Button)v.findViewById(R.id.button);
button.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
((OnButtonClickListener), mActivity).loadSecondFragment();
}
public interface OnButtonClickListener
{
void loadSecondFragment();
}
}
You should be able to just create the second fragment and have it loaded in the activity when a button is clicked.
Good luck.
I have a performance issue when using MapFragment together with the action bar menu.
The bug emerges when three conditions are met
Have a MapFragment instantiated.
Trigger a fragment transaction from the options menu, replacing the map fragment with another fragment.
Hit the back button and return to the map fragment. The performance is now noticeably degraded. Panning and zooming is very jerky.
Opening the options menu again and dismissing it again fixes the issue.
The behavior does not arise when
Triggering the fragment replacement from a view button instead from the options menu.
Triggering the fragment replacement right in onCreate()
replacing the blank fragment with MapFragment from the options menu
calling popBackStack from the options menu
using a ListFragment instead of a MapView
Minimal working example (requires access to Google Maps API):
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import com.google.android.gms.maps.MapFragment;
public class MapFragmentBugActivity extends Activity {
Fragment mMapFragment;
String MAP = "Map";
String BLANK = "Blank";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_bug);
mMapFragment = new MapFragment();
getFragmentManager().beginTransaction()
.replace(R.id.main, mMapFragment)
.commit();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(MAP);
menu.add(BLANK);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Fragment fragment;
if (item.getTitle().equals(MAP)) {
fragment = mMapFragment;
} else {
fragment = new Fragment();
}
getFragmentManager()
.beginTransaction()
.replace(R.id.main, fragment)
.addToBackStack(null)
.commit();
return true;
}
}
Activity layout, nothing special
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true" />
The fragment transaction is performed before the options menu is closed, this causes the weird behavior.
Instead of directly performing the fragment transaction, post it on the Handler. Once the options menu is closed, then the fragment transaction will be performed.
Try this :
#Override
public boolean onOptionsItemSelected(MenuItem item) {
final Fragment fragment;
if (item.getTitle().equals(MAP)) {
fragment = mMapFragment;
} else {
fragment = new Fragment();
}
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
getFragmentManager()
.beginTransaction()
.replace(R.id.main, fragment)
.addToBackStack(null)
.commit();
}
});
return true;
}
Background:
I'm using PreferencesFragment to manage preferences in my android app and i use options menu to access those preferences.
MainActivity class overriden methods:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.menu_prefs:
PrefsFragment prefs = new PrefsFragment();
getFragmentManager()
.beginTransaction()
.replace(R.id.main_container, prefs)
.addToBackStack(null)
.commit();
break;
}
return true;
}
res/menu/menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/menu_prefs"
android:icon="#android:drawable/ic_menu_preferences"
android:title="#string/menu_prefs_title"
android:showAsAction="ifRoom|withText">
</item>
</menu>
Problem:
While on main screen, I click preferences icon in action bar, PrefsFragment starts.
While on PrefsFragment, I click preferences icon in action bar PrefsFragment starts again (this is where problem arise).
I hit the back button and it reverts fragment transaction and goes back to PrefsFragment. (it should go back to main screen).
Question:
Is there any smart way to disable double showing PrefsFragment?
Desired outcome:
Clicking prefs icon in action bar if on main screen shows PrefsFragement, clicking prefs icon in action bar while PrefsFragemnt is visible does nothing, clicking back button while on PrefsFragment always goes back to main screen.
Ok, after some more digging I did this:
I'm disabling action bar/options menu if PrefsFragemnt is visible ( I could add different menu here but disabling it works fine in this case ).
PrefsFragment.java
import android.os.Bundle;
import android.preference.PreferenceFragment;
public class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
}
#Override
public void onStop() {
super.onStop();
getActivity().invalidateOptionsMenu();
}
}
MainActivity.java (overriden methods):
#Override
public boolean onCreateOptionsMenu(Menu menu) {
PrefsFragment prefs = (PrefsFragment)getFragmentManager().findFragmentByTag("PREFS");
if (prefs != null){
if (prefs.isVisible()) {
return true;
}
}
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.menu_prefs:
PrefsFragment prefs = new PrefsFragment();
FragmentManager manager = getFragmentManager();
manager
.beginTransaction()
.replace(R.id.main_container, prefs, "PREFS")
.addToBackStack(null)
.commit();
manager.executePendingTransactions();
invalidateOptionsMenu();
break;
}
return true;
}
The most important part is calling manager.executePendingTransactions() and invalidateOptionsMenu() on button click and calling invalidateOptionsMenu() when stopping fragment. This will cause for menu to be refreshed evry time we go to prefs and after getting out of them.
If you have better solutions I would love to see them, as this one feels a little bit hackish to me.
Can you try to accomplish it using FragmentTransaction.detach?
http://developer.android.com/reference/android/app/FragmentTransaction.html#detach%28android.app.Fragment%29