I am attempting to expand my application by adding a TabHost and some tabs to navigate extra features. The current app basically searches a database. The current application workflow:
App loads to a login screen
User logs in
User gets a search form and inputs data, presses "search"
Search loads a list activity of results...
With the new tabs, there is a separate tab for searching. I want all the seach activities to remain inside that tab group. So I've created an activity group to handle all of these:
public class searchGroup extends ActivityGroup {
public static searchGroup group;
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.history = new ArrayList<View>();
group = this;
View view = getLocalActivityManager().startActivity("search", new Intent(this,search.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
replaceView(view);
}
public void replaceView(View v) {
history.add(v);
setContentView(v);
}
public void back() {
if(history.size() > 0) {
history.remove(history.size()-1);
setContentView(history.get(history.size()-1));
}else {
finish();
}
}
#Override
public void onBackPressed() {
searchGroup.group.back();
return;
}
}
In my search activity's Search button onClickListener:
view = searchGroup.group.getLocalActivityManager().startActivity("search_results",new Intent(search.this, search_results.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
searchGroup.group.replaceView(view);
This is where I get the crash:
02-11 13:43:49.481: E/AndroidRuntime(1165): java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.myApp/com.myApp.search_results}:
android.view.WindowManager$BadTokenException: Unable to add window --
token android.app.LocalActivityManager$LocalActivityRecord#40543360 is
not valid; is your activity running?
However, if I uncomment a line from the search_result activity's onCreate:
new LoadSearches().execute();
no crash, but I get nothing obviously. LoadSearches() is an AsyncTask that does the heavy lifting of going out to the server and running the search string and then populating the returned data into the ListActivity in onPostExecute().
I don't quite understand why its crashing here and not normally when I switch activities. How should I tackle this? Is there a better way? I've read a little bit about Fragments but haven't done anything with it yet.
I have decided, after much pulling my hair out, to go with fragments. Some resources I found useful for converting my existing app to use Fragments and tabs:
Fragments in Android 2.2.1, 2.3, 2.0. Is this possible?
http://www.e-nature.ch/tech/?p=55
http://thepseudocoder.wordpress.com/2011/10/04/android-tabs-the-fragment-way/
I also had an issue with pass data between my activities. The way to pass data between activities using an intent/bundle doesn't really work the same but can modified slightly and still work.
The old way (passing data from Activity1 to Activity2):
Activity1
Intent myIntent = new Intent(search.this, search_results.class);
Bundle b = new Bundle();
b.putString("SEARCHSTRING", strSearch);
myIntent.putExtras(b);
startActivityForResult(myIntent, 0);
Activity2
Bundle b = getIntent().getExtras();
strSearch = b.getString("SEARCHSTRING");
Using fragments I had to create an initializer for Activity2:
public search_results newInstance(String strSearch){
search_results f = new search_results();
Bundle b = new Bundle();
b.putString("SEARCHSTRING", strSearch);
f.setArguments(b);
return f;
}
using this, the new method using Fragments:
Avtivity1
Fragment newFragment = new search_results().newInstance(strSearch);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.realtabcontent, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
Activity2 (onCreateView)
Bundle b = getArguments();
strSearch = b.getString("SEARCHSTRING");
I hope this helps someone as it was difficult for me to find all this information in one spot.
Related
I got an assignment to create an application. At the Splash screen stage, it is instructed to get a list from SQLite while in a Splash screen, and pass it to a Fragment (I did so by using an Intent).
My question is why not get it from the Fragment rather than passing it from the Splash screen to the Main Activity, and from there to the Fragment? It may seem unnecessary if it wasn't for some reason that's unknown to me.
When looking for information on this question I couldn't find anything. I guess it didn't come up previously, or at least I couldn't find the phrasing that was previously used.
The method that gets the list and passes it to the MainActivity:
private void toMainActivity(ArrayList<Movie> moviesList) {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.putParcelableArrayListExtra("moviesList", moviesList);
startActivity(intent);
finish();
}
At MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Movie> moviesList = this.getIntent().getParcelableArrayListExtra("moviesList");
Bundle listBundle = new Bundle();
listBundle.putParcelableArrayList("moviesList", moviesList);
Fragment mlf = new MoviesListFragment();
mlf.setArguments(listBundle);
ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragments_container, mlf);
ft.commit();
}
At the Fragment:
Bundle listBundle = getArguments();
if (listBundle != null) {
moviesList = getArguments().getParcelableArrayList("moviesList");
moviesAdapter.attachMoviesList(moviesList);
}
That's usually a matter of making sure you're working on the same instance of something. When passing something in a Bundle you're securing that the next screen will work on the same instance of the passed data that the previous screen was.
Whether it's better or worse than querying a db every time is a case-by-case thing. Sometimes you want to maintain data "continuity" between screens and sometimes you want to get the newest copy of the data (which could've been modified asynchronously).
I have an activity which can be accessed via different activities.
Like you have one activity containing a listview and the second containing a gridview and both shows the same data. When you click on an item a new activity with some details is shown. I need to somehow remember which activity was the initial one (with gridview or listview) so that I can set a button to redirect here. But it's not enough to just return to previous activity (like using finish() to close the current one), because there is a way to navigate among different objects from inside the details activity (I have a gridview on that screen). So I need to remember the initial view during moving through the details activity for various number of times.
Is there a way?
EDIT: omg why so many downvotes? At least tell me why it is so stupid, I'm learning coding for Android for 2 weeks how am I supposed to know everything??
This sounds like it would best be solved by using two Fragments within the same Activity
You can use a bundle object to pass data to the new activity (B) so you could know wich activity (listView or gridView) had started it.
In activity B:
private static final String ACTIVITY_TYPE = "activity_type";
public static Intent createIntent(Context context,String activityType)
{
Intent intent = new Intent(context, ActivityB.class);
Bundle b = new Bundle();
b.putString(ACTIVITY_TYPE,activityType);
intent.putExtras(b);
return intent;
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle b = getIntent().getExtras();
String activityType;
if (b != null)
{
activityType = b.getString(ACTIVITY_TYPE);
}
//the calling activity didn't use the createIntent method and didn't provide the activity type
else
{
activityType = "some_default_type";
}
//save activity type to use later
//rest of your code
}
In the calling activity:
Intent intent = ActivityB.createIntent(ActivityListView.this,"activity_list_view");
startActivity(intent);
In my FirstActivity, user will log in. If the user exists in the database, it is loaded and should be "passed" to the SecondActivityFragment which is within the SecondActivity. The need is to check whether the user is with incomplete register, if so, the toolbar will display a warning menu item telling it to complete the registration.
┌FirstActivity
├─SecondActivity
└──SecondActivityFragment
Every tutorial that I see showing how pass data through Activity and Fragment talking about replace fragments and so on, I think that's not my case.
I created newInstance() on my SecondActivityFragment but I'm kinda lost.
public static SecondActivityFragment newInstance(User user) {
Bundle args = new Bundle();
args.putSerializable("user", user);
SecondActivityFragment fragment = new SecondActivityFragment();
fragment.setArguments(args);
return fragment;
}
And when user clicks in login button
if (userExists()) {
userManager = new UserManager();
User user = userManager.getByEmailPwd(editEmail.getText().toString(), editPwd.getText().toString());
Intent secondActivity = new Intent(getContext(), SecondActivity.class);
SecondActivityFragment.newInstance(user);
startActivity(secondActivity);
}
Try to put user into your secondActivity intent.
Then in the SecondActivity's onCreate method get the user class using getIntent().getSerializable() and create an instance of SecondActivityFragment.
Calling
SecondActivityFragment.newInstance(user);
that way, will not cause any effects in what will got presented.
If you want to present the fragment in the context of Second activity, consider passing the data that the fragment need to know to the Second activity - it should be sth like:
secondActivity.putSerializable("user", user)
Then in SecondActivity's onCreate, or in other method, you have to replace fragment being displayed, for your SecondActivityFragment instance:
User user = null;
final Bundle args = getIntent().getExtras();
if(args.getSerializable("user") instanceof User){
user = (User)args.getSerializable("user");
}
if(user != null){
Fragment secondActivityFragment = SecondActivityFragment.newInstance(user);
FragmentMenager fragmentMenager = getFragmentMenager();
FragmentTransaction fragmentTransaction = fragmentMenager.beginTransaction();
fragmentTransaction.replace(R.id.frame_for_your_fragment, secondActivityFragment);
}
I have a two class named Stock Details extends Fragment and another one is Stock info extends activity,
when I was trying to go back to my Stock details pages from Stock info pages it shows "Unfortunately,Application has stopped",but in my program I use
Intent intent = new Intent(this,StockDetails.class);
startActivity(intent);
finish();
for go back to fragment. but it does not work. help me to solve this problem
I am the beginner for android.
kindly help me to go back from an Activity to fragment
you do not need to start activity for StoreDetail as it is a subclass of Fragment not Activity.This is the reason that your app is crashed.
Now moving to your question if you want to go back to the fragment from the activty : Stock INFO you just need to call finish() it will finish the current activity(Stock Info) and the fragment which is in background will be resumed.I had same problem and solved by this way .This is the onCreate Method of Activity(in your case it is for StockInfo class).Have a look:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setmCallBack(new IResultCallback() {
#Override
public void result(Result lastResult) {
if (lastResult!=null) {
finish();// by this line I have killed the current activity
}
else
{
Toast.makeText(getActivity(), "NotScan: ", Toast.LENGTH_SHORT).show();
}
}
});
}
Ok look,
startActivity(intent);
this method name startActivity. So it will start another activity not fragment.
You can See & read the fragment:
http://developer.android.com/guide/components/fragments.html
Add fragment to go back to manually to the backstack.
FragmentTransaction transaction = getFragmentManager().beginTransaction();
YourFragmentName myFragment = new YourFragmentName();
transaction.replace(R.id.fragment_container, myFragment);
transaction.addToBackStack(null);
transaction.commit();
And There is several methods here. Just take a look:-
http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
If you want to go back to the fragment , you can do this :
getFragmentManager().popBackStack();
This question already has answers here:
Android: Pass data(extras) to a fragment
(3 answers)
Closed 9 years ago.
With Activities, I used to do this:
In Activity 1:
Intent i = new Intent(getApplicationContext(), MyFragmentActivity.class);
i.putExtra("name", items.get(arg2));
i.putExtra("category", Category);
startActivity(i);
In Activity 2:
Item = getIntent().getExtras().getString("name");
How do you do this using Fragments? I am using the compatibility library v4 also.
Does it go in the FragmentActivity? Or the actual Fragment?
And Which Method does it go in? onCreate? onCreateView? another?
And can I see example code please?
EDIT: It is worth noting I am trying to keep Activity 1 as an Activity (or actually ListActivity where I am passing the intent of the listitem when clicked) and then pass to a set of tabbed-fragments (through a Fragment Activity) and I need either tab to be able to get the extras. (I hope this is possible?)
you can still use
String Item = getIntent().getExtras().getString("name");
in the fragment, you just need call getActivity() first:
String Item = getActivity().getIntent().getExtras().getString("name");
This saves you having to write some code.
What I tend to do, and I believe this is what Google intended for developers to do too, is to still get the extras from an Intent in an Activity and then pass any extra data to fragments by instantiating them with arguments.
There's actually an example on the Android dev blog that illustrates this concept, and you'll see this in several of the API demos too. Although this specific example is given for API 3.0+ fragments, the same flow applies when using FragmentActivity and Fragment from the support library.
You first retrieve the intent extras as usual in your activity and pass them on as arguments to the fragment:
public static class DetailsActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// (omitted some other stuff)
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(
android.R.id.content, details).commit();
}
}
}
In stead of directly invoking the constructor, it's probably easier to use a static method that plugs the arguments into the fragment for you. Such a method is often called newInstance in the examples given by Google. There actually is a newInstance method in DetailsFragment, so I'm unsure why it isn't used in the snippet above...
Anyways, all extras provided as argument upon creating the fragment, will be available by calling getArguments(). Since this returns a Bundle, its usage is similar to that of the extras in an Activity.
public static class DetailsFragment extends Fragment {
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
// (other stuff omitted)
}