I have two different activities, A & B.
Both have NavigationDrawer , look alike, but not are the same, because I could not get the drawer layout ID of activity A in activity B.
Both activity has 3 fragments each (total 6).
The NavigationDrawer contains all fragments of activity class A only. My problem is, when I am in activity B, and try to open one fragment of activity A from navigation drawer, it throws an error
No view found for id 0x7f090047 (com.wlodsgn.bunbunup:id/linear) for fragment FmMenu{b1e537f0 #0 id=0x7f090047}
How do I achieve it?
I have created an intent inside the second activity and started the activity A with information about the fragment to be called.
Intent i = new Intent(this, ActivityClass.class);
i.putExtra("frgToLoad", "FRAGMENT_A");
startActivity(i);
Now, inside activity A, checked the extra and load the right Fragment:
OnCreate(){
...
if (getIntent().getExtras() != null) {
String intentFragment = getIntent().getExtras().getString("frgToLoad");
switch (intentFragment){
case "FRAGMENT_A":
// Load corresponding fragment
break;
case "FRAGMENT_B":
// Load corresponding fragment
break;
case "FRAGMENT_C":
// Load corresponding fragment
break;
}
}
}
One have to check if intent is null or not before I try to assign a value to intentFragment. This is because that line of code is called whether I am coming from activity B or not, and it will throw error if intent is null.
credit : https://stackoverflow.com/a/36064344/3380537
Related
I have an activity A which uses a fragment A which has a list. The activity A can call on a search activity. The problem that I am seeing is if I were to go the search activity and then go back to the activity A, from there fragment A gets loaded. If I select an item from Fragment A i get taken to Fragment B, if i were to press the back button i have to click it 2 or 3 times. Any ideas? Do i need to start the search activity with a parameter so it does not add to the back stack. I have tried the Flag to Intent.FLAG_ACTIVITY_CLEAR_TOP when starting the search activity but the issue occurs.
Java:
Activity A:
public void popFragment(){
if(getSupportFragmentManager().getBackStackEntryCount() > 1) {
getSupportFragmentManager().popBackStack();
getSupportFragmentManager().executePendingTransactions();
}
}
Fragment A:
private void showSearchActivity(){
Intent intent = new Intent(context, SearchActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(intent, 0)
}
I have an android application with three activities the flow is like this
A->B->C
The problem is when i go from Activity C to Activity B. ( B<-C)
DETAILS AND BACKGROUND:
I Have an arraylist of of Albums that contain a list of photos, that contain a list of tags. Activity A deserializes the ArrayList to see if there are any new changes in the ArrayList. Activity A handles adding new albums, after adding albums I serialize the ArrayList, this works. I select an album which takes me to Activity B(Arraylist gets passed to activity B, successfully). In this Activity(B) I can add images. After adding images i serialize the Arraylist. This works, i can switch between activities A and B and the photos get saved. When I select the back button on activity B it takes me to Activity A, I do so by doing the following
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
case R.id.action_add:
.
.
.
case:
etc,
.
.
}
}
(EACH activity has the android back button in the top left hand corner)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
In doing so I learned control flow goes into Oncreate() inside of Activity A as though a new instance is being created?
We are now inside of Activity B
Now If I select an image it takes me to Activity C.In this Activity(C) I can add multiple Tags. I serialize the ArrayList, then i click the back button taking me to Activity B, now that i am in activity B I click the same photo which then takes me to Activity C, but the tags are not displayed associated to the image, If i hard code tags they show up. But it seems as though the arraylist never gets updated. I don't know where control flow goes Back inside of Activity B, If i choose to take the path with OnCreate() A new instance gets created making my list object null
intent = getIntent();
arrayList = (ArrayList)intent.getSerializableExtra("key1");
becomes null, Since a new instance of Activity B gets created, getting invoked By C ,this ArrayList never gets passed to Activity B, because only Activity A calls B in that fashion right?
I want Activity C to be able to save the Arraylist properly, When i click the back button taking me to activity B, and re click the same photo i expect to see the tags pertaining to the image
Activity A:
Calls activity B by Intent
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("key1", list); //list is an ArrayList
I pass a list to activity B
Activity B:
control flow then goes into Oncreate();
This method OnCreate() contains
intent = getIntent();
arrayList = (ArrayList)intent.getSerializableExtra("key1");
I receive the arraylist succesfully.
now I want to pass the list to Activity C
Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
intent.putExtra("key2", list);
Activity C
{
control flow then goes into Oncreate();
This method OnCreate() contains
intent = getIntent();
arrayList = (ArrayList)intent.getSerializableExtra("key2");
now when i press the back button, i do so by doing this
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);//control goes to onCreate()??
case R.id.action_add:
.
.
.
case:
etc,
.
.
}
}
Now lets take away NavUtils.NavigateUpFromSameTask(this);
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case android.R.id.home:
//NavUtils.navigateUpFromSameTask(this);
finish();
case R.id.action_add:
.
.
.
case:
etc,
.
.
}
}
In doing so I get back to Activity B WITHOUT entering onCreate(), but if i click the photo the tags don't show up.
}
Still inside of activiy C
I want to add back navigation to toolbar. I need to get from a fragment in an activity to a specific fragment in another activity.
It looks a little like this, where every orange line means navigating to a new activity or fragment:
How do I move from fragment B to fragment A from OtherActivity?
Consider these steps:
From Activity 1 holding Fragment A , you want to directly load Fragment B in Activity 2.
Now, I am thinking first, then you press a button in Fragment A, you can directly go to Activity B.
Then it means, you can simply load Fragment B as soon as you arrive in Activity 2.
Since you are dealing with back navigation (I believe you mean the upNavigation?), you can override the following:
But watch clearly, because if you need to load an exact fragment in Activity 2, you need to know somehow:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, Activity2.class);
intent.putExtra("frag", "fragmentB");
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
As you can see, when you click the back arrow on the toolbar, we pass a value through our intent to identity which fragment we want to load.
Next, in your Activity2, simply get the intent extra and do a switch or an if statement:
#Override
public void onResume(){
super.onResume();
Intent intent = getIntent();
String frag = intent.getExtras().getString("frag");
switch(frag){
case "fragmentB":
//here you can set Fragment B to your activity as usual;
fragmentManager.beginTransaction().replace(R.id.container_body, new FragmentB()).commit();
break;
}
}
From here, you should have your Fragment B showing in Activity 2.
Now you can handle the same thing while inside Activity 2 to decide where to go when a user clicks the back home arrow!
I hope this helps you get an idea.
Note: I thought about the interface approach and realized it is not necessary since this can be done easily with this approach!
To navigate from one Activity to another Activity's Fragment, with Kotlin version 1.4.0 and, for example, calling a click listener on a button it works so:
binding.yourButton.setOnClickListener {
supportFragmentManager.beginTransaction().replace(R.id.yourLayout, NameOfYourFragment()).commit()
}
Use this code to change your fragment
fragmentManager.beginTransaction().replace(R.id.container_body, new FragmentC()).commit();
and to show navigation on custom toolbar add
Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true)
((AppCompatActivity)getActivity()).getSupportActionBar().setHomeButtonEnabled(true);
I have followed the official documentation http://developer.android.com/guide/practices/tablets-and-handsets.html for Tablet support to create dual pane layout that works as shown below, in that in small screens (phones) it uses one Fragment inside one Activity to display a list of objects and another fragment inside another Activity.
Every other documentation I read talks about a one way flow from Master to details, now I want to go back the other way, from details to master and I am stuck.
In the details, I have added an Item that I want to display in the list and I want this to be dynamic such that I can add few items and each time I hit save I want the List to grow.
This is what I have done so far
In FragmentA(List Fragment) I have a method that (re)loads the data and call notifyDataSetChanged on the adapter.
I added a method in the call back that is called each time an item is added. And both Activity A and Activity B implements this listener
So when I add an item in FragmentB(Details Fragment) I call the listener and on Activity A which is housing the dual pane layout I try this
public void OnNewCustomerAdded() {
Fragment frag = null;
frag = getSupportFragmentManager().findFragmentByTag("CustomertListFragment");
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.detach(frag);
ft.attach(frag);
ft.commit();
}
Unfortunately that throws a NPE, and also if I call the methods directly in the Fragment to reload data, that throws an NPE. The only thing that works with some side effects is this
public void OnNewClientAdded() {
Intent intent = getIntent();
finish();
startActivity(intent);
}
So how can I safely restart a Fragment inside an Activity without restarting the other Fragment.
Wow, this is quite tricky, never expected it to be this challenging. Well this is how I solve it.
First I removed the second activity and reworked the code to show single pane in handheld devices and dual pane in tablets using just one Activity instead of two. This is not necessary but it helps when you are dealing with one set of lifecycles and listeners.
Then to actually have items added in the DetailsFragment appear immediately in the ListFragment while still having the Details Fragment open. I finished the containing Activity, restarted it and passed it an intent that tells it to start up the DetailsFragment
Remember that the ListFragment is set to start up no matter which device size. So you just need to start the DetailsFragment and since there is already a call back that does that it makes it easy so here is the code
//Callback method for when an item is added, called from the Details
//Fragment
public void OnNewCustomerAdded() {
Intent mIntent = getIntent();
mIntent.putExtra(Constants.SHOULD_START_CUSTOMER_DETAILS, true);
finish();
startActivity(mIntent);
}
Then in the onCreate of the Activity, after the ListFragment has been started, you do this
boolean shouldStartCustomerDetails = getIntent().getBooleanExtra(Constants.SHOULD_START_CLIENT_DETAILS, false);
if (shouldStartClientDetails){
OnCustomerListItemSelected(0);
}
The OnCustomertListItemSelected is the standard mCallback listener that you get if you created a Master/Details Activity in Android Studio, I just modified it to suit my app like so
/**
* Callback method from {#link OnCustomerListItemSelectedListener}
* indicating that the item with the given ID was selected.
*/
#Override
public void OnCustomerListItemSelected(long id) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
CustomerDetailsFragment fragment = CustomerDetailsFragment.newInstance(id);
getSupportFragmentManager().beginTransaction()
.replace(R.id.customeractivity_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
CustomerDetailsFragment fragment =
CustomerDetailsFragment.newInstance(getIntent().getLongExtra(Constants.ARG_ITEM_ID, 0));
getSupportFragmentManager().beginTransaction()
.add(R.id.customeractivity_list_container, fragment)
.commit();
}
}
In the main activity ActivityA I replace FragmentA by fragment FragmentB. From FragmentB the user can start a new activity ActivityB. By hitting the back button in ActivityB, ActivityA is displayed showing FragmentA. I was expecting to see FragmentB with its last state. Do I have to save the state of the previous activities separately to provide this behaviour?
ActivityA(FragmentA) -> ActivityA(FragmentB) -> ActivityB
BACK
ActivityA(FragmentB)
In the main activity I set the current fragment using:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.a_main_frame_content, new FragmentB())
.addToBackStack(null)
.commit();
From the fragment I start a new activity using:
Intent intent = new Intent(getActivity(), ActivityB.class);
getActivity().startActivity(intent);
ActivityA is set as parent activity for ActivityB to provide proper navigation.
[UPDTATE] It looks like the problem lies in the different behaviour of navigating back and navigating up. If I navigate back, the activity is displayed in its last state while navigating up forces the activity to recreate.
Lets try this:
In the intent of the parentActivity(if you can set it before you create parentActivity its best, otherwise you may have to use setIntent):
currentActivityIntent.putExtra("random-unique-key-for-each-activity",
random-unique-key-for-each-activity);
And before you create a child activity, u put following in a map:
myKeyIntentMap.put(random-unique-key-for-each-activity, currentActivityIntent);
In the method triggered on "Up" event :
{
String parentKey = currentActivity.parentActivity.getIntent.getStringExtra("random-unique-key-for-each-activity");
Intent intentToLaunch = (Intent)myKeyIntentMap.get(parentKey);
intentToLaunch.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP );
startActivity(intentToLaunch);
}
This way, using the intent, even if your History Stack is A-someAct1-someAct2-B, and u launch intent resolving to A, it will be "brought to front" killing someActs.
P.S. I havent done any null checks and havent kept in mind the exact method names, just given you an approach.
Behaviour of "up" is sometimes misleading indeed. When I faced similar problem I preferred to save my time and not deal with saving states.
You can quickly solve it by catching your navigation "up" event in your Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
}
return true;
}
This is happening because when you are moving to ActivityB activityA is going to puse and destroy state respectively, thus when you back to activity ActivityA ActivityA is starting again thus you are getting fragmentA as view. you need to save the state using sharedPfer. Use some flag to save the state in your onCreateView() check the state and set correct fragment for the view. Hope you have got my point.
public static final String PREFS_NAME = "mypref";
boolean isVisited;
//check sharedpref
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
isVisited= settings.getBoolean("isVisited", false);
if(!isVisited){
// set fragmentA
}else{
// set fragmentB
}
// inside fragment transaction block
Editor edit = settings.editor();
isVisited.setBoolean(true);
edit.commit();