I am calling an activity from within itself - basically i've a list of new storys and two filter buttons that when clicked restart the activity with an intent passed that changes the news stories.
When i run the app it works, but for a second i get the old activity UI while the app reads from the new xml feed and then the UI updates. Is there any way to stop this from happening and get the activity to restart cold.
here's the code I am currently attaching to the onclicklistener
public void openFootballNews(View v) {
Intent i = new Intent(this, News_Landing.class); // News_landing class is the class this code is in
Bundle bundle = new Bundle();
bundle.putString("code", "football"); // this, if set, changes the xml feed to read
i.putExtras(bundle);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
this.onCreate(null); //this has halved the time the old UI is on the screen for but I cant get rid of it completely
startActivity(i);
}
any help would be great, thanks!
Starting an activity from itself doesn't make much sense (unless your aim is to do something esoterically recursive ;) ). Also, I may be mistaken, but I believe activities are kept in a stack so that as you flip between news stories, you're piling up one nearly-identical activity after another. I'd similarly think calling onCreate() by hand is bad form.
Would need to see all of your code, but my guess is that you are reading your feed and creating your list inside onCreate(), and that your best bet is to refactor that into a openNews(String sport) method, which you call once in onCreate() and again in your listener(s).
Related
I have an app that hold post information in an activity. in this activity related posts listed in bottom of post. User by clicking on related post can go to post activity and see that post info and related posts too.
As you can see in image, I have Activity A that holds post and it's related posts. When user Click on post I send user to Activity A with new post id and fill activity by new data.
But I think this is not Right way!
should I used Fragment instead of Activity?
Opening another Instance of an Activity on top of another is simplest way of navigating a content graph. User can simply press back, and go to previously opened content, until user reaches back to starting Activity, then the application closes. Though pretty straight forward, this particular approach has two issues:
It may happen that a lot of Instances of same activity are on the stack, utilising a large amount of device resources like memory.
You don't have a fine grained control over Activity Stack. You can only launch more activities, finish some, or have to resort to intent flags like FLAG_CLEAR_TOP etc.
There is another approach, that re-uses the same Activity instance, loads new content in it while also remembering the history of content that was loaded. Much like a web browser does with web page urls.
The Idea is to keep a Stack of content viewed so far. Loading new content pushes more data to stack, while going back pops the top content from stack, until it is empty. The Activity UI always displays the content from top of the stack.
Rough Example:
public class PostActivity extends AppCompatActivity {
// keep history of viewed posts, with current post at top
private final Stack<Post> navStack = new Stack<>();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get starting link from intent extras and call loadPost(link)
}
private void loadPost(String link){
// Load post data in background and then call onPostLoaded(post)
// this is also called when user clicks on a related post link
}
private void onPostLoaded(Post post){
// add new post to stack
navStack.push(post);
// refresh UI
updateDisplay();
}
private void updateDisplay(){
// take the top Post, without removing it from stack
final Post post = navStack.peek();
// Display this post data in UI
}
#Override
public void onBackPressed() {
// pop the top item
navStack.pop();
if(navStack.isEmpty()) {
// no more items in history, should finish
super.onBackPressed();
}else {
// refresh UI with the item that is now on top of stack
updateDisplay();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
// cancel any background post load, release resources
}
}
I would choose:
activity/fragment depends on complexity with:
horizontal recyclerview with custom expanded card view
and inside this expanded card view second vertical recyclerview :)
Here's what you can try.
Create a PostActivity which is a shell for fragments. Inside this activity you can just replace fragments using FragmentTransaction.
Your PostActivity can now have a PostFragment which will hold post and related posts. Now on click of post you can replace PostFragment with PostDetailFragment with postID being sent to the new fragment as a bundle. The PostDetailFragment will now display details according to id.
Check here: http://www.vogella.com/tutorials/Android/article.html#components_fragments
By seeing the picture the way i would implement is i would have create an activity with a bottom listview for your items and on top there would be a framelayout for holding fragments . when user click on any list item i would load the respective fragment in the activity
It all depends on what you are trying to achieve. What would you expect to happen when the user touches the back button after going down a couple of levels? If you want to the application to exit, no matter how deep in the sequence they have gone, then the best solution in my opinion is to simply reload the same activity with the new data and invaliding the affected views. If you need the back button to take the user back to the previous data, then the next question would be if you are keeping track of the past data breadcrumb. If so, then just intercept the back button and load the previous data for as long as there is data in your stack, or exit if you get to the top. If you don't want to keep track of the previous data chain, then instead of loading one activity with the new data, you can start a new activity of the same class, but with the new data. Android with keep the track of activities and each back button touch would close the running activity and take the user to the previous activity. Choice of activity versus fragment is just yours. You can use fragments that hold the data that you want to change after each user touch, create new ones when needed, disconnect the previous ones, and connect the new ones. You will need to do some extra work to make sure the back button works correctly (depending on you want the back button to behave). Based on what I can tell, it is simpler to just have one activity and load new data when needed and keep a trail of data changes, if you need to be able to go back.
It can be achieved using activity alone. Though I preferred moving all related UI to fragment.
You can use Navigator class.
Here the steps:
1. Add Navigator Class
public class Navigator {
private static Navigator instance;
public synchronized static Navigator getInstance() {
if (instance == null) {
instance = new Navigator();
}
return instance;
}
public void navigateToActivityA(Context context) {
Intent activity= AActivity.getCallingIntent(context);
context.startActivity(activity);
}
}
2. Add the calling method to your Activity class.
public static Intent getCallingIntent(Context context) {
return new Intent(context, AActivity.class);
}
3. Call the activity with the following code in your caller activity.
Navigator.getInstance().navigateToActivityA(this);
I suggest that you read about AndroidCleanArchitecture
For this task...
0) Starting new activity
I read again about question, and understood that you need advice for starting activity. So, starting new activity it's Ok, your main problem will be with another things (see below).
But lets talk about starting another data. Using Fragment instead doesn't resolve your task, fragments helps with different screen work. Using for example just data refreshing as a variant. You may use just single activity and refresh only data, it will look much better, if you also add animation, but not better than starting activity.
Using Fragment helps your with different screen actions. And maybe, answering on your question - it will be most suitable solution. You just use single acitivity - PostActivity, and several fragments - FragmentMainPost, FragmentRelated - which will be replaced, each other, by selecting from related post.
1) Issues with returning back
Lets imagine, that users clicks to new one activity and we loaded new data. It's Ok, and when Users clicks over 100 activities and receiving a lot of information. It's Ok, too. But main question here it's returning back (also another about caching, but lets leave it, for now).
So everyone know, it's bad idea to save a lot of activities in stack. So for my every application, with similar behavior we override onBackPressed in this activity. But how, lets see the flow below:
//Activities most have some unique ID for saving, for ex, post number.
//Users clicks to 100 new activities, just start as new activity, and
//finish previous, via method, or setting parameter for activity in AndroidManifest
<activity
noHistory=true>
</activity>
....
//When activity loaded, save it's activity data, for ex, number of post
//in some special place, for example to our Application. So as a result
//we load new activity and save information about it to list
....
// User now want return back. We don't save all stack this activities,
// so all is Ok. When User pressing back, we start action for loading
//activities, saved on our list..
.....
onBackPressed () {
//Getting unique list
LinkedTreeSet<PostID> postList =
getMyApplication().getListOfHistory();
//Getting previous Post ID based on current
PostID previousPostID = postList.get(getCurrentPost());
//Start new activity with parameter (just for ex)
startActivity(new Intent().putExtra(previousPostID));
}
RESULT
I found this as the best solution for this tasks. Because in every time - we work only with single activity!
To implement the up navigation, I would like to go back to a specific activity on the history stack. If the activities on the stack are implemented by different classes, it works like this (assuming I have activities A, B and C on the stack and want to go back to activity A:
protected void onUpPressed() {
Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
Android will pop activities off the stack until the activity specified by the intent is the top most (activity implemented by class A in this case).
However, my app has several activities on the stack implemented by the same class. That's because they display the same kind of data but for different objects. They were launched with an intent that specified both the class implementing the activity and the object to display (either in the extras bundle or in the data property).
Now I'm looking for code to again pop several activities off the history stack until the matching activity the top most. If I extend the above code and additionally set the extras bundle or the data property, it doesn't work. Android always matches the first activity implemented by the specified class and doesn't go back far enough. The extras bundle and the data property are ignored.
protected void onUpPressed() {
Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse("myapp:" + rootId));
startActivity(intent);
finish();
}
So how can I achieve to go back to a specific activity? What intent fields does Android compare to determine if it has found the desired activity?
As you described, you have one Activity that show different content based on the starting Intent. Well, why not Fragments?
I don't know the details of your applications architecture, but is should be easy to refactor that Activity to a Fragment. There could be a FragmentActivity that wraps all the Fragments which responsible for showing the content. This way you would have much more freedome to handle the activity's stack of fragments.
Steps summarized:
Convert your existing Activity (that show the content) to a Fragment.
Make a FragmentActivity (that will manage the Fragments).
Make the FragmentActivity "singleInstance", so it will cache all the
"startActivity" requests, where you have the opportunity to add a
new Fragment representing the new content to show.
You could add fragments this way:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// ContentFragment is the Fragment that you made from your Activity.
// Here you can pass the intent that stores the object to show also,
// so the parsing of the intent would be the same.
ContentFragment fragment = ContentFragment.newInstance(intent);
getFragmentManager()
.beginTransaction()
.add(fragment, null)
.addToBackStack("id here that will be used at pop")
.commit();
}
And you can pop to a specific id this way:
getFragmentManager().popBackStack("id here", 0);
This solution has a side-effect. The fragments will stick together, so you cannot insert any other Activity between them. This is trivial, but worth mentioning since differs from your current implementation.
I also assumed that you are familiar with how "singleInstance" and Fragments work. Fell free to ask if something is not clear.
To implement FLAG_ACTIVITY_CLEAR_TOP, Android searches through your task's activity stack (from top to bottom) using the following test:
if (r.realActivity.equals(newR.realActivity)) {
Your problem is that realActivity is a ComponentName (so the above comparison locates the topmost activity in the stack that matches on package and class name): no further test is performed against the intent, so it is impossible to be any more specific about which of that component's activities you wish to target.
Therefore:
So how can I achieve to go back to a specific activity?
There is no native means of accomplishing this. Your best bet is probably to manually implement a form of bubbling as suggested by #VM4.
What intent fields does Android compare to determine if it has found the desired activity?
Only the component name.
I recommend storing your data model outside of the extras, using a singleton to access, and refreshing an activity using that data model on the onResume()
Lets say you are in /home/usr/vm4/development and in this activity you have somekind of Views (lets say TextViews) that let you go to all parent directories. (Clicking on for example "usr" will go to /home/usr. In windows it looks like this:
I don't think Android lets you back into a specific Activity based on extra data. However you can do a trick here. The View (that when clicked takes you somewhere) can have a String tag attached to it:
TextView link = new TextView();
link.setTag("/home/usr");
Now when you click this View in the onClick() method:
onClick(View v) {
String extra = v.getTag();
// start your activity with extras here.
}
Now you just need to make sure you add the right tags to your links when inflating the activity.
I am using ActivityGroup. I use the following code from ActivityGroup in order to replace view and launch a new activity.
Intent i = new Intent(SummaryCostScreen.this,PermissionsScreen.class);
replaceContentView("activity1",i);
public void replaceContentView(String id, Intent newIntent) {
View view = getLocalActivityManager().startActivity(id,
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
}
The problem with the above code is that, I need to have startActivityForResult in place of startActivity, since I need to update the UI of launcher activity when coming back from the launched activity.
getLocalActivityManager() does not have startActivityForResult. How should I address this situation, such that, I am able to update UI from onActivityResult?
Any help is much appreciated.
PS: I cannot change the replaceContentView approach for launching new screen, since that has been used at numerous other places and this is the only scenario in which I need to call startActivityForResult
Converting comments as answer,
Try using onResume() to update UI of SummerCostScreen
Im working on a little game and having some issues.
There is the Menu
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch (arg0.getId()){
case R.id.bStartGame:
Intent a = new Intent(Menu.this, Action.class);
startActivityForResult(a, 1);
break; }
then the activity which starts a surfaceview
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
}
and then the the surfaceView with the game mechanics.
Most of my code is in this view.
Now I have the problem to find a good solution for the gameoverscreen.
If I start a new activity inside the surfaceview, it works - but i dont get the result() which is the score achieved during a session.
So now I wanted to ask you guys how to solve this issue.
I thought of a way, but dont know how to implement it.
It would be to pass the highscore from the surfaceview to the activity and set it as a result(which the menu activity gets back) there.
And start an xml file via dialog, which would be the gameoverscreen and as soon as the player touches the back button he gets back to the menu where he can see his achieved score.
Can you please tell me how to code this?
Kind regards
Denis
There's a number of ways to solve this:
-use startActivityForResult and then send it back from your new activity, catching it in the old activity using onActivityResult (check https://developer.android.com/reference/android/app/Activity.html)
-do what i did (the lazy, hacky way :): start the new activity with startActivity() and add the highscore as extra data added to the intent. In your new activity, use getIntent().getInt (ow whatever) to get the sent score data and do with it what you will. Then close that activity and you'll return to the previous one holding your surfaceview.
NOW THE TRICK: before you start your new activity with it's score added to the intent, just run the same score calculation in your surfaceview's activity as you would in your new activity! That way, when you return to your surfaceview's activity, you will still have the correct, new score (if stored/onresume'd correctly; don't forget to add it to your save/restore state and/oror the surfaceview's private variables)!
The only downside is that you'll have two location you have to update your scoring mechanics at. And it's not good programming. But it works and it's easy.
[Update Solution]
Referring to the post in the link
ViewPager PagerAdapter not updating the View
public void onSomeButtonClicked(View view) { // registered to Button's android:onClick in the layout xml file
Log.w(TAG,"Some button clicked !!");
getIntent().setAction(IntentManager.Intents.ACTION_SPAWN_LIST_BY_SOMETHING);
mViewPager.getAdapter().notifyDataSetChanged();
}
// And inside my PagerAdapter
#Override
public int getItemPosition(Object object) {
return 0;
}
Fixed all my problems, i just used Intent.setAction().
[Update to below Post]
My problem is i have a ViewPager PagerAdapter in my Main Activity. On clicking one of the 3 buttons., any specific intent will be fired (i used intent, i could have gone with just State variable as well, just that i pass some Uri with the intent). What happens is., i do
public void onSomeButtonClicked(View view) { // registered to Button's android:onClick in the layout xml file
Log.w(TAG,"Some button clicked !!");
getIntent().setAction(IntentManager.Intents.ACTION_SPAWN_LIST_BY_SOMETHING);
mViewPager.getAdapter().notifyDataSetChanged();
}
This is why i was guessing maybe i should just do startActivity again, with the new Intent Action on the same Activity.
Spawning a new Activity, i would have to redraw every other view in the layout which is basically the same code, for the most part in the current activity.
Any suggestions here? Please help
[Original Post]
I have a PagerAdapter and 3 Buttons in the my Main Activity. This activity is enter from Main Launcher.
When i press any one of the buttons, the Intent Action is changed.
My question:
The changed Intent action reflects some changed view in the ViewPager and does_not spawn a new Activity as such, only the view is updated.
What approach should i take to get this task?
Can i start the currentActivity using startActivity() and different Intent actions on button click?
or is there any other efficient way in android to do this?
[No need code, just explanation of logic / method would suffice]
Thanks in advance
If you are saying that you are trying to use startActivity to bring up the same activity again, and its not working, it could be because you set something like singleTop in your Android manifest.
If you are asking whether or not you should use an intent to change the state of your Activity, then the answer is "it depends". If the user would expect the back button to return your app to its previous state (instead of going back to the home screen), then it might be a good choice for you. If that is the case, however, I would ask why not just make 2 different Activities? Otherwise, just do as Dan S suggested and update the state of your Activity as the user interacts with it.
You can always use the onNewIntent() hook. Do something like this in your activity:
protected void onNewIntent(Intent intent){
//change your activity based on the new intent
}
Make sure to set the activity to singleTop in your manifest. Now whenever startActivity is called the onNewIntent() will be executed. Also, note that per the documentation:
Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent.