I am using a ToolBar Spinner to switch among the 6 activities of my App. The opening activity, which I call MainView, has only the spinner [plus a help link on the ToolBar action menu]. The user selects from the spinner which of the other 5 activities he wishes to run and when finished uses the ToolBar back button of that activity to return to MainView.
It all works fine except, after returning to MainView from any of the other activities, the name of that activity remains displayed in the Spinner, not MainView as i had expected. Plus, if user wants to return to that same activity he must select any of the others first.
I had thought that when the App returns to MainView using the ToolBar back button it would do so by calling the MainView OnCreate, and the MainView spinner would be recreated thus displaying MainView. But this appears to be not the case.
I have tried a few things including setSelection(0) in onCreate, re-initializing the spinner in onStart and onResume - but none has made a difference. Hope you can help.
xml for spinner ...
<Spinner
android:id="#+id/spinner_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
initialization code for spinner
String[] mainview_names = new String[]
{"MainView", "WebView", "JsoupView", "CodeView", "Connectivity", "FtpView"};
Spinner mainSpinner = (Spinner) findViewById(R.id.spinner_main);
ArrayAdapter<String> adapter = new ArrayAdapter<>
(this,android.R.layout.simple_spinner_dropdown_item, mainview_names);
mainSpinner.setAdapter(adapter);
mainSpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener());
// mainSpinner.setSelection(0);
The Listener ...
public class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch (pos){
case 0: // Main View
break;
case 1: // Web View
BrowseWWW();
break;
case 2: // Jsoup View
LoadHTMLJsoup();
break;
case 3: // Code View
LoadHTMLCode();
break;
case 4: // Connectivity
GetHTMLConn();
break;
case 5: // Ftp View
GetHTMLFtp();
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
}
EDIT: Additional information:
The 5 activities each respond to the back button with code structured like this.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
help.setVisibility(View.GONE);
switch (item.getItemId()){
case android.R.id.home: // up navigation
finish();
return true;
case R.id.load:
// ....
}
return true;
case R.id.Clear
// ...
return true;
}
return false;
}
#Override
public void finish() {
Intent i = new Intent();
i.putExtra("urlKey", url.getText().toString());
i.putExtra("tokenKey", token.getText().toString());
i.putExtra("redirectsKey", HttpURLConnection.getFollowRedirects());
setResult(RESULT_OK, i);
super.finish();
}
Ok the spinner state issue:
Without digging into more of your code, here's my theory:
You're in activity A. You select something in the spinner, say B. Now you're in activity B and activity A is paused retaining state it had -- i.e. it has the item corresponding to B selected in the spinner. When you go back from B, activity A is resumed in that exact state and, of course, still has the B item in the spinner. You can't go to B again right away, because if you re-select B, your select handler will not be called (since there's no change).
The fact that Activity is not destroyed every time is probably what tricked you, that's why you should take a look at google guide on Activities -- it will probably save you a lot of guessing time :)
So, to fix your issue with spinners, you should override the onResume() method of each activity and use it to set the spinner to the state corresponding to that activity -- that's the short version.
Long version is, you're probably going somewhat against the flow using toolbar spinner to navigate between activities.
Navigation can be faster and more intuitive you implement your Web, Jsoup, Code and other top-level views as Fragments (also, this) inside a single Activity, that's exactly what they're for. It'll probably be more user-friendly to use Tabs instead of Spinner.
Related
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);
When transitioning between pages, each fragment page shares the same menu options. The menu option contains button which launches an activity. That activity is highly dependent on the information the current page item shows.
I noticed that when I change the view such that, the next page is almost shown on the screen (not fully transitioned) and I select the option for that item, the options shows the data from the previous item. I think this is because, he transition between pages was not fully complete. This is kind of confusing for my users. There could be people who swipe faster and press the option button. I noticed then when swiping between fragments and then suddenly press the menu option. The options shows the data from the previously active page.
If I could only hide the menu options, and only show it when the page is fully transitioned, I believe I can solve this problem. Or else, maybe I am doing something wrong which could have averted this in the first place?
I am using FragmentStatePagerAdapter.
Thank you!
Use ViewPager.SimpleOnPageChangeListener and when the onPageScrollStateChanged event is triggered check to see the state of the scroll if is idle then the page is in view and active (allow user to press the menu button), if is in another state then lock/hide the menu buttons.
I think I was able to solve my problem. I created an interface on my fragment which the hosting activity has to implement:
public interface OnOptionsMenuEnabledListener{ public boolean onOptionsMenuEnabledListener(); }
The hosting activity will just return a flag indicating if the menu is enabled.
#Override
public boolean onOptionsMenuEnabledListener()
{
return mOptionsEnabled;
}
And set the flag via ViewPager.onPageScrollStateChanged (int state):
#Override
public void onPageScrollStateChanged(int state)
{
switch(state)
{
case ViewPager.SCROLL_STATE_IDLE:
mOptionsEnabled = true;
break;
case ViewPager.SCROLL_STATE_DRAGGING:
mOptionsEnabled = false;
break;
case ViewPager.SCROLL_STATE_SETTLING:
mOptionsEnabled = true;
break;
}
}
Every time the option is being selected during screen transition, I call the interface method to communicate to me what the status of the flag:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
boolean enable = true;
if(mOnOptionsMenuEnabledListener != null)
{
enable = mOnOptionsMenuEnabledListener.onOptionsMenuEnabledListener();
}
if(enable)
{
...
// select your menu items
}
return true;
}
So in this approach, the menu is still there (which is a big plus). But the option menu doesn't react until it settles down on a particular page.
With this I no longer encounter the problem.
I hope this help someone in the future!
Cheers!
When the button is clicked check ViewPagers current item -
https://developer.android.com/reference/android/support/v4/view/ViewPager.html#getCurrentItem()
YourObject object = yourList.get(yourViewPager.getCurrentItem());
I have a DrawerLayout which contains a ListView of items that link to other Activities. The OnItemClick event of the ListView is a switch-case for each menu element, which runs the code
...
case targetActivity:
listViewInstance.ClearChoices();
drawerInstance.CloseDrawer(MainMenuLayout);
drawerInstance.RequestLayout();
StartActivity(targetIntent);
break;
...
This works mostly as expected, but if I change device orientation and targetIntent returns to the base activity with the ClearTop flag, the DrawerLayout is still open! In spite of the very explicit call to CloseDrawer, and despite the fact that I can see it closing as the new activity is starting. What am I doing wrong? (I am using Xamarin.Android, if this is relevant).
[Edit 1: I suspect that CloseDrawer only registers the drawer as closed when the closing animation is completed, and that the base activity is paused before this occurs. If I call CloseDrawer in a different thread and pause the current thread for a moment before the call to StartActivity, the drawer works as expected, although of course this is not a suitable solution. Does anyone have a better idea how to solve this in a clean fashion?].
[Edit 2: One possible solution, linked here, is to create an event listener for the drawer's OnDrawerClosed event:
public class DrawerListener2 : DrawerLayout.SimpleDrawerListener
{
public override void OnDrawerClosed(View view)
{
base.OnDrawerClosed(view);
var listParent = (RelativeLayout)view;
var listViewInstance = listParent.FindViewById<ListView>(Resource.Id.mainmenu);
int selectedIndex = menuListView.CheckedItemPosition;
switch (selectedIndex)
{
case 0:
// Start activity A
break;
case 2: // Element 1 is a menu spacer
// Start activity B
break;
case 3:
// Start activity C
break;
}
}
}
...
// In base activity's OnResume:
drawerInstance.SetDrawerListener(new DrawerListener2());
...
This has two problems that make it a no-go: I have to recreate my menu item order in the switch-case since I can't reach the object that contains this information in the OnDrawerClosed handler, and I also can't start the required activities from the event handler since these require intent information from the base activity].
Solved by semi-ugly hack:
In base activity, create property that stores its value as an Extra:
private bool ForceDrawerMenuCloseOnActivityResume
{
get { return Intent.GetBooleanExtra("ForceDrawerMenuCloseOnActivityResumeKey", false); }
set { Intent.PutExtra("ForceDrawerMenuCloseOnActivityResumeKey", value); }
}
In the base activity's OnResume(), re-close the drawer if this property is set:
if (ForceDrawerMenuCloseOnActivityResume) {
drawerInstance.CloseDrawer(MainMenuLayout);
ForceDrawerMenuCloseOnActivityResume = false;
}
When closing drawer, immediately before starting the activity that causes the drawer to remain open on activity resume:
case targetActivity:
drawerInstance.CloseDrawer(MainMenuLayout);
listViewInstance.ClearChoices();
ForceDrawerMenuCloseOnActivityResume = true;
StartActivity(targetIntent);
I am creating an android application with three tabs using PageSlidingtabStrip as a library to create a swipe view.And it has three fragments.Each fragments has a list view.When the item of the listview is clicked it opens an activity and display the details.
The problem is how can i come back to the fragment in the main screen using back button in actionbar in the activity
And how can i go to the corresponding Fragment(Tab)
Try something like this :
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
Intent intent = new Intent(YourCurrentClass.this , ClassThatYouWantToGo.class);
startActivity(intent)
}
Or actually like #TommyTopas said, you can just Override onBackPressed and put this.finish();.
EDIT
As I've understood you want to use a button on your AcitonBar, then you have tod o something like this :
First set the HomeButton enabled doing :
getActionBar().setDisplayHomeAsUpEnabled(true); Then Override onOptionsItemSelected
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// or onBackPressed();
this.finish()
}
return true;
}
As I understand, when you return to the "Tab" Activity, you want to display the same tab in which the list item had been clicked. What you can do is, when a list item in any tab is clicked, save the tab number in onSavedInstanceState(), and when the Activity is recreated, then set the previously selected tab (if one was selected previously). You will get the savedInstanceState that you saved in onSavedInstanceState() back in the onCreate() of the same Activity.
You can provide an Up navigation by writing getActionBar().setDisplayHomeAsUpEnabled(true); and then in the onOptionsItemSelected method in the activity, if the item's id is android.R.id.home call the activity's method onBackPressed(); which will close your current activity and come back to your fragment
So at the moment I have an activity that can be reached from two different activities, the problem is that I can only set one activity as the parent activity in the manifest XML file. Obviously this is bad UX/UI design because the activity may send the user back to the wrong activity they were at previously and so I'm trying to dynamically set which activity is the parent activity.
The trouble is I'm not quite sure how to go about this, whether in code or XML so any pointers are appreciated. :)
For future readers here's an example of how to actually implement the official/proper solution as per the developer guides (scroll to the paragraph beginning with "This is appropriate when the parent activity may be different...").
Note that this solution assumes you are using the Support Library to implement your ActionBar and that you can at least set a 'default' parent Activity in your manifest XML file to fallback on if the Activity you are backing out of is in a 'task' that doesn't belong to your app (read the linked docs for clarification).
// Override BOTH getSupportParentActivityIntent() AND getParentActivityIntent() because
// if your device is running on API 11+ it will call the native
// getParentActivityIntent() method instead of the support version.
// The docs do **NOT** make this part clear and it is important!
#Override
public Intent getSupportParentActivityIntent() {
return getParentActivityIntentImpl();
}
#Override
public Intent getParentActivityIntent() {
return getParentActivityIntentImpl();
}
private Intent getParentActivityIntentImpl() {
Intent i = null;
// Here you need to do some logic to determine from which Activity you came.
// example: you could pass a variable through your Intent extras and check that.
if (parentIsActivityA) {
i = new Intent(this, ActivityA.class);
// set any flags or extras that you need.
// If you are reusing the previous Activity (i.e. bringing it to the top
// without re-creating a new instance) set these flags:
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// if you are re-using the parent Activity you may not need to set any extras
i.putExtra("someExtra", "whateverYouNeed");
} else {
i = new Intent(this, ActivityB.class);
// same comments as above
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
i.putExtra("someExtra", "whateverYouNeed");
}
return i;
}
NOTE: If you do not set a default parent Activity in the manifest XML file then you'll also need to implement onCreateSupportNavigateUpTaskStack() since the system will have no idea how to generate a backstack for your task. I have not provided any example for this part.
My thoughts on the finish() type solutions
On my searching for a solution to this problem I came across a few answers that advocate the strategy of overriding onOptionsItemSelected() and intercepting the android.R.id.home button so they could simply finish() the current Activity to return to the previous screen.
In many cases this will achieve the desired behavior, but I just want to point out that this is definitely not the same as a proper UP navigation. If you were navigating to the child Activity through one of the parent Activities, then yes finish() will return you to the proper previous screen, but what if you entered the child Activity through a notification? In that case finish()ing by hitting the UP button would drop you right back onto the home screen or whatever app you were viewing before you hit the notification, when instead it should have sent you to a proper parent Activity within your app.
Like this way you can navigate dynamically to your parent activity:
getActionBar().setDisplayHomeAsUpEnabled(true);
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
NOTE: It redirects you to the activity or fragment where you came from, no matter whether it's a parent or not. Clicking on the action bar Up/Home button will just finish the current activity.
There are two concepts in play here 'Up' and 'Back'. 'Back' is the obvious one: take me to where I was just before I came here. Usually you don't need to be concerned with 'Back', as the system will handle it just fine. 'Up' is not so obvious - it's analogous to Zoom Out - from an element to the collection, from a detail to the wider picture.
Which of these fits your use case?
As per comment below: the up button pulls the destination from the android manifest, but it can be customized programmatically.
The method to override is getParentActivityIntent.
Here is my code and works perfectly fine.
#Override
public Intent getParentActivityIntent() {
Intent parentIntent= getIntent();
String className = parentIntent.getStringExtra("ParentClassSource");
Intent newIntent=null;
try {
//you need to define the class with package name
newIntent = new Intent(OnMap.this, Class.forName(className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return newIntent;
}
From the parent activities;
Intent i = new Intent(DataDetailsItem.this, OnMap.class);
i.putExtra("ParentClassSource", "com.package.example.YourParentActivity");
startActivity(i);
To find out how to use Up Navigation properly see this Android Dev Guide.
Note that there is a big flaw in the above Android Dev Guide as the NavUtils functions work differently for ICS(and lower) and JellyBean(and higher). This flaw in NavUtils is explained beautifully here.
Generally, a 'detail' type of activity will have to provide the 'up' navigation if it has nested/related contents. The 'back' navigation is handled by the system so you really don't have to worry about it.
Now for the extra effort to support the 'up' navigation, there are a few ways of doing it:
Activity that has the parent activity defined in the AndroidManifest.
Your Android Manifest
---------------------
<activity
android:name="com.example.app.DetailActivity"
android:parentActivityName="com.example.app.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.app.MainActivity" />
</activity>
Your Detail Activity
--------------------
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
This works well if there's only one parent activity meaning if the (DetailActivity) always gets launched from (MainActivity). Otherwise this solution will not work if (DetailActivity) gets launched from different places.
More here: http://developer.android.com/training/implementing-navigation/ancestral.html
(Easier and Recommended) Activity with Fragment and Fragment back-stack:
Your Detail Activity
--------------------
protected void replaceFragment(Bundle fragmentArguments, boolean addToBackStack) {
DetailFragment fragment = new DetailFragment();
fragment.setArguments(fragmentArguments);
// get your fragment manager, native/support
FragmentTransaction tr = fragmentManager.beginTransaction();
tr.replace(containerResId, fragment);
if (addToBackStack) {
tr.addToBackStack(null);
}
tr.commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
In this solution, if the user presses 'back', the fragment will be popped from the fragment backstack and the user is taken back to the previous fragment while still remaining in the same activity. If the user presses the 'up', the activity dismisses and the user is lead back to the previous activity (being the parent activity). The key here is to use the Fragment as your UI and the activity as the host of the fragment.
Hope this helps.
You can override the Up button to act like the back button as following:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
You will need to keep track of the parent activity. One way to do this is by storing it as an extra in the intent you create to start the child activity). For example, if Activity A starts Activity B, store A's intent in the intent created for B. Then in B's onOptionsItemSelected where you handle the up navigation, retrieve A's intent and start it with the desired intent flags.
The following post has a more complex use-case with a chain of child activites. It explains how you can navigate up to the same or new instance of the parent and finish the intermediate activities while doing so.
Android up navigation for an Activity with multiple parents
Kotlin 2020
My activity launches from different activities so the AndroidManifest only works with 1 parent activity.
You can return to the previous activity like this:
supportActionBar?.setDisplayHomeAsUpEnabled(true)
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item!!.itemId){
android.R.id.home -> {
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}