UPDATE: I have found the header isn't the only thing affected, but listeners and other elements in the fragment aren't being initialized. (Such as FAB's). The only things that function correctly is anything built inside the adapter (i.e. the ListView rows)
The Problem: Open Nav Drawer, click on a list item that opens up a new Fragment in the same Activity. Open the Nav Drawer again, click the same item that should simply replace the existing Fragment and while the Fragment does open, all of the elements that are set programmatically in Java (text, colors, listeners) are not changed/added to the Fragment. The view is simply the default xml layout. I have confirmed with Log.d that the code that sets these things is being ran. (If you open a different Fragment and then go back to the original one, everything works fine)
The Confirmed Cause: When changing this:
compile "com.android.support:appcompat-v7:25.0.1"
compile 'com.android.support:design:25.0.1'
to
compile "com.android.support:appcompat-v7:25.1.0"
compile 'com.android.support:design:25.1.0'
(if only one of these libraries is changed, it doesn't matter which one, the problem still occurs)
I have confirmed changing these two libraries is the problem (also confirmed this problem in a second app -- and changing only this code, has the exact same result)
My questions is, why is this happening and how can it be fixed?
The Code and more detailed description from original question:
FragmentTransaction tItems = fm.beginTransaction();
ListFragment mFrag = new PlannerFragment();
tItems.replace(R.id.main_frag, mFrag, TAG_CALC_FRAGMENT);
tItems.commit();
There are about 4 options in my drawer that all open this Fragment, but before each one, I set a global variable like this:
MyApp.PlanType = "highbal";
MyApp.PlanType = "lowbal";
etc.
Based on the PlanType value (above), that Fragment is loaded with different data (but, in the exact same xml layouts). It is simply a ListView with a header.
This Fragment, (PlannerFragment) loads fine the first time.
When I click on another item (a different PlanType) that goes to the same Fragment (same code above executed): The ListView loads fine in the actual list. But in the header in the list, none of the setText() values set the new data properly. It uses the default values from my xml layout.
Now, if I load a different Fragment in between, everything works great. It's only when I either add or replace (I've tried both) the same Fragment on top of the old one does this happen.
Here is header code at top of PlannerFragment
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Setup listview
lv = getListView();
LayoutInflater inflaterHeader = getActivity().getLayoutInflater();
ViewGroup header = (ViewGroup) inflaterHeader.inflate(R.layout.planner_listview_header, lv, false);
lv.addHeaderView(header, null, false);
// Load Data then Set Textviews, e.g.:
TextView title = (TextView) getActivity().findViewById(R.id.tvPlannerTitle);
dateOutput = (TextView) getActivity().findViewById(R.id.tvDebtOutDate);
If I add to that code, dateOutput.setText("test"); it does not happen. I can do a Log.dand my data registers, so this code is being read, but the UI is not set.
I've even tried removing the Fragment first like this with a check:
Fragment fragment = fm.findFragmentByTag(TAG_CALC_FRAGMENT);
if(fragment != null)
fm.beginTransaction().remove(fragment).commit();
This has no effect.
Again, this ONLY happened when I changed the libraries above. Is this a bug with the library or does it now respond differently?
There is many changes and issues with Fragments on appcompat 25.1.0. The first one seems related to your issue but the two next should be read as it may be important.
FragmentTransaction replace behaviour
Replace not working properly in appCompat 25.1.0
Current status (Jan 4 2017): bug assigned
Issue link
Fragment lifecycle change on 25.1.0
onStart on new Fragment is called before onStop on previous one
Current Status : works as expected. For me it could be a breaking change from app compat 25.0.x
Issue link
There is also an issue with the resume event called two times in fragment, see here.
TLDR. Maybe the best temporary solution is to not use 25.1.0 until a new version is out.
you can add list_view_header.xml in navigationDrawer() method and change the position of selected item like selectItem(position - 1).
private void navigationDrawer() {
mTitle = mDrawerTitle = getTitle();
// mNavigationDrawerItemTitles = getResources().getStringArray(R.array.navigation_drawer_items_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
LayoutInflater inflater = getLayoutInflater();
View listHeaderView = inflater.inflate(R.layout.list_view_header, null, false);
mDrawerList.addHeaderView(listHeaderView, null, false);
setupToolbar();
DataModel[] drawerItem = new DataModel[6];
drawerItem[0] = new DataModel(R.drawable.dashboard_icon, "Dashboard");
drawerItem[1] = new DataModel(R.drawable.cal_icon, "Calender");
drawerItem[2] = new DataModel(R.drawable.classes, "Classes");
drawerItem[3] = new DataModel(R.drawable.message_icon_hdpi, "Message Center");
drawerItem[4] = new DataModel(R.drawable.profile, "Profile & Setting");
drawerItem[5] = new DataModel(R.drawable.bulletin_icon, "Bulletin Board");
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(true);
DrawerItemCustomAdapter adapter = new DrawerItemCustomAdapter(this, R.layout.list_view_item_row, drawerItem);
mDrawerList.setAdapter(adapter);
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setDrawerListener(mDrawerToggle);
setupDrawerToggle();
}
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position - 1);
}
}
private void selectItem(int i) {
Fragment fragment = null;
switch (i) {
case 0:
fragment = new Fragment_DashBoard();
// toolbar.setTitle("Dashboard");
Titletv.setText("Dashboard");
break;
case 1:
fragment = new Fragment_Calender();
// toolbar.setTitle("Calender");
Titletv.setText("Calender");
break;
case 2:
fragment = new Fragment_Classes();
// toolbar.setTitle("Classes");
Titletv.setText("Classes");
break;
case 3:
fragment = new Fragment_MessageCenter();
// toolbar.setTitle("Message");
Titletv.setText("Messages");
break;
case 4:
fragment = new Fragment_ProfileSetting();
// toolbar.setTitle("Profile");
Titletv.setText("Profile");
break;
case 5:
fragment = new Fragment_BulletinBoard();
//toolbar.setTitle("Bulletin Board");
Titletv.setText("Bulletin Board");
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction trans = fragmentManager.beginTransaction();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).addToBackStack(null).commit();
mDrawerList.setItemChecked(i, true);
mDrawerList.getItemAtPosition(i);
// setTitle(mNavigationDrawerItemTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
Log.e("MainActivity", "Error in creating fragment");
}
}
Related
I had a navigation menu, which displays various modes of operation to the user. When the user selects a specific option, the activity is to load the corresponding fragment (Eg: Add Product should load the AddProduct fragment).
The loading works fine - I used FragmentManager together with FragmentTransaction to replace the current fragment with the new fragment. The problem comes in when the screen gets rotated - the current two fragments I have used, display on top of each other.
Here is the code I have set up for the NavigationDrawerListener (the part that does the loading):
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
DrawerLayout drawerLayout = (DrawerLayout) currentActivty.findViewById(R.id
.homeDrawer);
TextView heading = (TextView) currentActivty.findViewById(R.id.AppHeadingTextView);
switch (position)
{
case 0:
heading.setText("Home");
drawerLayout.closeDrawers();
UserMainScreenFragment homeScreen = new UserMainScreenFragment();
FragmentManager homeManager = currentActivty.getFragmentManager();
FragmentTransaction homeTransaction = homeManager.beginTransaction();
homeTransaction.replace(R.id.contentArea, homeScreen);
homeTransaction.commit();
break;
case 2:
heading.setText("Add a Product");
drawerLayout.closeDrawers();
AddProductFragment newProductFragment = new AddProductFragment();
FragmentManager manager = currentActivty.getFragmentManager();
FragmentTransaction replace = manager.beginTransaction();
replace.replace(R.id.contentArea, newProductFragment);
replace.commit();
break;
}
}
You need to create a separate layout for required activity according to orientation.
In your activity you need to check for savedInstanceState being null to avoid adding the same fragments again:
//when the orientation changes, the savedInstanceState bundle is not null
if(savedInstanceState!=null){
//add your fragment to the layout
}
another thing, I noticed you reference the fragment manager through currentActivty.getFragmentManager();
maybe the currentActivty.getFragmentManager(); has an old reference to the activity that gets destroyed, you should also use getSupportFragmentManager() if you're using the support library
You have to add different layout's (with same name) for both orientations, then Android will automatically inflate the suitable xml according to the orientation.
File structure will look like this,
res
- drawable
- layout // for portrait
- layout_name.xml
- layout-land // for landscape
- layout_name.xml // same name should be used
- .....
Find a sample solution here
I am attempted to create an application in Android studio that has a navigation drawer.
I am using Android Studio (beta)0.8.14. In this version, there is a navigation drawer activity. I been able to set the labels for my navigation drawer menu using this piece of code and the corresponding values in my strings file
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.login);
break;
case 2:
mTitle = getString(R.string.sign_up);
break;
case 3:
mTitle = getString(R.string.view_map);
break;
case 4:
mTitle = getString(R.string.about);
break;
case 5:
mTitle = getString(R.string.version);
}
}
It looks really nice, but I can't figure out how to add onClickListeners for each of the items.
I've also added this in my NavigationDrawerFragment.java (which was automatically created by Android Studio):
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mDrawerListView = (ListView) inflater.inflate(
R.layout.fragment_navigation_drawer, container, false);
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
});
mDrawerListView.setAdapter(new ArrayAdapter<String>(
getActionBar().getThemedContext(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
new String[]{
getString(R.string.login),
getString(R.string.sign_up),
getString(R.string.view_map),
getString(R.string.about),
getString(R.string.version),
}));
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
return mDrawerListView;
}
I would like to launch a different activity for each of the list items but I can't understand how and unfortunately I can't seem to find a tutorial that uses Android Studio's built-in navigation drawer activity.
Also, is it possible to have this navigation drawer available on all of my activities? Do I need to create a new navigation drawer fragment every time I create a new activity?
Thanks in advance!
The idea of having a navigation drawer is to use fragment. It wouldn't be very efficient to launch a new activity whenever you click an item in your navigation drawer.
In your selectItem(position) method you could execute some code for creating a new fragment for list item in your navigation drawer. Each navigation item should be a different fragment then just use a fragment transaction to add it to the container view of the main activity.
As a simple example of what the method may look like:
private void selectItem(int position) {
FragmentManager fragmentManager = getFragmentManager();
switch(position) {
//fragment for position 0
case 0:
fragmentManager.beginTransaction()
.replace(R.id.container, new Fragment0())
.commit();
break;
//fragment for postion 1
case 1:
fragmentManager.beginTransaction()
.replace(R.id.container, new Fragment1())
.commit();
break;
//fragment for position 2
case 2:
fragmentManager.beginTransaction()
.replace(R.id.container, new Fragment2())
.commit();
break;
default:
break;
}
}
This may not be exactly what you want but it is an option for what you're trying to accomplish.
As a note in order for onSectionAttached() to work, all your framents will have to call that to pass their title to the main activity.
See this link for more info.
In order to put onclicklisteners on your navigation drawer, just put an intent below the line of code where you are changing the title bar i.e. in the switch case block
e.g.
case 1:
mTitle = getString(R.string.login);
Intent transfer = new Intent(HomeFragment.this,NextActivity.class);
startActivity(transfer);
This will do it for you.
In order to create a Navigation Drawer, and because it seemed confusing, I used Eclipse's automatically created template. However, this template creates code so that it displays the content of a String array adapter, and at the selection of an item a Fragment is instantiated in the attached Activity like this:
#Override
public void onNavigationDrawerItemSelected(int position)
{
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = null;
switch (position)
{
case 0:
fragment = new FragmentType0();
break;
case 1:
fragment = new FragmentType1();
break;
case 2:
fragment = new FragmentType2();
break;
default:
Log.w(this.getClass().getSimpleName(), "Reached Default in onNavigationDrawerItemSelected!");
break;
}
if (fragment != null)
{
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.container, fragment);
ft.addToBackStack(null);
ft.commit();
mTitle = getString(((GetActionBarTitle) fragment).getActionBarTitleId());
restoreActionBar();
}
}
Because the generated template is the following:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mDrawerListView = (ListView) inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
selectItem(position);
}
});
String[] drawerTitles = new String[] { getString(R.string.drawer_string_0), getString(R.string.drawer_string_1), getString(R.string.drawer_string_2)};
mDrawerListView.setAdapter(new ArrayAdapter<String> (getActionBar().getThemedContext(), android.R.layout.simple_list_item_1,
android.R.id.text1, drawerTitles));
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
return mDrawerListView;
}
So as you can see, the titles are given in the navigation drawer, displayed and specified as a String, and when you select an item, the callback is called on the Activity, at which based on position of selected item, the 'proper' Fragment is instantiated based on an if-else structure. This seems like a fairly fragile solution, but I can't seem to figure out how to make it better.
My hunch is something like using a FragmentStatePagerAdapter, but I'm at a loss, as I've never used one before, especially not in this particular context (I'm REALLY new to the navigation drawer, and I don't know how I would integrate it with the navigation drawer, so that it displays strings that correspond to each Fragment). Can anyone please provide advice on what to do here? It works, but it doesn't seem like the "proper" solution.
I'd reccomend that you stay away from the auto-generated code. A lot of the time, it isn't at all what you want.
I recommend you take a look at this tutorial. It explains the basics of the navigation drawer pretty well.
I have my navigation drawer set up just like the one in the android developers training.
Inside the navigation drawer i have two fragments right now. One of the fragments contains a listview, whose content is populated through an AsyncTask, which loads the items through a rather time consuming file system search. I already achieved to retain my list items through a configuration change:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null) {
new LoadPdfList().execute(Environment.getExternalStorageDirectory());
} else {
pdfThumbs = savedInstanceState.getParcelableArrayList("pdfThumbs");
}
}
But there is still one big problem, which i can't get solved. As soon as i switch my current Fragment with the navigation drawer and switch back to my previous fragment, which hosts the listview, the asynctask is reexecuted.
Although this behavior is not really surprising to me considering this part of the displayView-Code from the android developer tutorial:
private void displayView(int position) {
// update the main content by replacing fragments
Fragment newFragment = null;
switch (position) {
case 0:
newFragment = new DocumentsFragment();
break;
case 1:
newFragment = new PlayerFragment();
break;
default:
break;
}
if (newFragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, newFragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
currentFragmentId = position;
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
I tried messing around with the different possibilities from the transactionmanager but still couldn't get it running.
I just want my fragments being retained while changing the my fragments through the navigation drawer.
I used Navigation Drawer to setup a Drawer.
In my MainActivity on the onCreate method, I have the following setup
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FrameLayout frame = new FrameLayout(this);
frame.setId(CONTENT_VIEW_ID);
setContentView(frame, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
if (savedInstanceState == null) {
setFragment(0);
}
}
And my setFragment Method, within the Main Activity is this:
public void setFragment(int id) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch(id){
case 0:
fragmentTransaction.add(CONTENT_VIEW_ID, FragOne.newInstance()).commit();
break;
case 1:
fragmentTransaction.replace(CONTENT_VIEW_ID,FragTwo.newInstance()).commit();
break;
}
}
note I've declared CONTENT_VIEW_ID as:
private static final int CONTENT_VIEW_ID = 666;
When the application starts, it loads the fragment one and it works fine.
When I swipe open the Navigation drawer and select the position 1, it replaces the current fragment with the FragTwo, but right after that the navigation drawer freezes, I can't access it anymore.
Can anyone help me resolve this issue, I been searching online for awhile now and couldn't find a solution to my problem, that why I have decided to ask here, thank you in advanced :)
Asuming you have all the other needed code t make a drawerLayout work fine, I think you should use .replace(...) in both cases. It works for me, even though it freezes a little while fragment is being load before it finishes closing.