I have 4 ImageButtons placed in a simple Layout. When I click any of these buttons, a new TabActivity is launched, and the corresponding Tab is selected (0-3). The tabs are styled with an ImageView matching one of the previous buttons each.
EDIT WITH SOME PROGRESS BELOW
TL;DR: How to set a different SharedElementTransition when returning to the calling Activity, so that the "back" transition is not a reverse of the original one, and possibly animate different Views?
I also use a SharedElementTransition to animate the ImageButton into the selected tab, which works when entering the new Activity, but if I navigate through the Fragments of the new Activity, and press the back button from a Fragment different from the one I arrived to, the reverse SharedElementTransition doesn't match the Tab I'm currently on, and it just reverses the transition used to launch the new activity.
I can see this is the expected behavior, but how can I override it? I want the reverse transition to start from the current Tab, not from the Tab I arrived at from the previous Activity.
I hope this ignominious picture helps understanding the question (it shows the behavior I want to obtain -starting new activity from the orange button, navigating to the green tab's fragment, and pressing back returning to the first activity transitioning from the green tab-):
There's no much code involved in the question, but just in case I am launching the new Activity as:
(...)
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(fromActivity, sharedView, transitionName);
fromActivity.startActivityForResult(toIntent, requestCode, options.toBundle());
And then, in the arriving Activity, I set up the TabLayout as follows in the onCreate() method (analogous piece of code for each Tab:
tab0 = (...);
ImageView v0 = (ImageView) tab0.findViewById(R.id.tab_img);
ApiLevelHelper.setImageDrawable(this, v0, R.drawable.menu_btn_0);
tabLayout.getTabAt(0).setCustomView(tab0);
Thanks in advance
EDIT - SOME PROGRESS ACHIEVED
Well, There's still an important problem to solve, but I've managed to get the right Tab transitioning on the reverse animation. In order to achieve this, I have no transition name previously attached to the Tab's ImageViews, and I set the transition name on the onCreate() method of the new activity only to the selected Tab. Then inside the onBackPressed() call, I clear all transition names from all Tab's ImageViews, and set it to the current Tab. This way the reverse animation starts from the correct Tab, but it ends up in the original ImageView's position, instead of the new one.
What I've tried so far:
Clearing transition names on the first activity and setting it just to the ImageView I want to reverse-transition to, inside the onActivityResult(...) method. Sadly enough, the animation conditions seem to be stablished before this method is called.
Staticly referencing the parent activity (knowing this is bad, just testing) and clearing transition names and setting just the one I want before calling super.onBackPressed() from the second Activity. It didn't change anything.
Some code showing how the transition name of the current Tab is set:
//Second Activity
#Override
public void onBackPressed() {
if (ApiLevelHelper.isAtLeast(Build.VERSION_CODES.LOLLIPOP)) {
for (ImageView v : tabImages) {
v.setTransitionName(null);
}
int selectedTab = mTabLayout.getSelectedTabPosition();
tabImages[selectedTab].setTransitionName(getString(R.string.transition_menu_tab));
Intent output = new Intent();
output.putExtra("selected_tab", selectedTab);
setResult(RESULT_OK, output);
}
super.onBackPressed();
}
And then, in the first Activity...:
//First Activity
//This code has no effect right now on the ending position of the transitioned view.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQ_WHICH_TAB && data != null) {
if (ApiLevelHelper.isAtLeast(Build.VERSION_CODES.LOLLIPOP)) {
int selectedTab = data.getIntExtra("selected_tab", 0);
setTransitionName(selectedTab); //This call nullifies all transition names
//and sets it back to the right ImageView.
//But it doesn't work.
}
}
super.onActivityResult(requestCode, resultCode, data);
}
I think I somehow need to manipulate the previously calculated transform... still investigating.
Related
I have a list of products, if I click on one, the image of the product is transitioned into the detail screen.
And if I go back, the image is transitioned back to the list.
This works fine.
The problem is that when I scroll down in my detail screen, the image is no longer visible.
But when I go back to the list screen the image is still transitioned, resulting is a buggy transition.
Video example here
I want to achieve something like the Play Store
Where there is no return animation if the image is no longer visible.
Code
Starting detail activity:
Intent intent = new Intent(getActivity(), ProductDetailActivity.class);
intent.putExtra(ProductDetailActivity.EXTRA_PRODUCT, product);
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
productViewHolder.getProductCover(), productViewHolder.getProductCover().getTransitionName()).toBundle();
getActivity().startActivity(intent, options);
In DetailActivity I set the transition name:
coverImageView.setTransitionName(getString(R.string.transition_key_product_cover_with_id, product.getId()));
styles.xml:
<item name="android:windowContentTransitions">true</item>
Any idea how to implement the behaviour I want to achieve?
With the following link you can know if a view inside an scroll is visible or not: https://stackoverflow.com/a/12428154/4097924
Then you can make a simple method to know if your imageView is visible inside the scrollview, similar to this easy example:
public boolean isVisibleInsideScroll(){
Rect scrollBounds = new Rect();
scrollView.getHitRect(scrollBounds);
if (imageView.getLocalVisibleRect(scrollBounds)) {
// Any portion of the imageView, even a single pixel, is within the visible window
return true;
} else {
// NONE of the imageView is within the visible window
return false;
}
}
Then I see two possible options that I have not proved:
option 1: overwrite the method onBack (and every way to go back if you have another one). Inside the method, you can assign the transition when the element it is visible before leaving the screen:
#Override
public void onBackPressed(){
if(isVisibleInsideScroll()){
coverImageView.setTransitionName(getString(R.string.transition_key_product_cover_with_id, product.getId()));
}
super.onBackPressed();
}
option 2: overwrite the method onScroll (and every time the scrollview is scrolled) you can register or unregister the animation if the view is visible.
The code in this option is similar to the previous one.
Good luck! I like a lot your animation, I saw it in youtube. :)
I have a RecyclerView with images and when I press an image the app opens another activity that contains a ViewPager with the same images but in the position of the one I selected.
I've done the transition in Lollipop to share this image between activities using supportPostponeEnterTransition and supportStartPostponedEnterTransition in the called activity to wait until the viewPager is loaded with images to start the transition.
When I enter in the called activity and when I press back the transitions are ok.
The problem I'm facing is if I move to another image in the ViewPager of the called activity, when I press back it animates the image that it was selected at the beginning, not the currently selected one.
I've been able to change the animated image to the one selected in the called activity with this:
#Override
public void onBackPressed() {
View view = ((ImageDetailFragment) adapter.getFragment(viewPager,
viewPager.getCurrentItem())).getTransitionView();
ViewCompat.setTransitionName(view, Constants.TRANSITION_IMAGE);
super.onBackPressed();
}
But it is returning to the same position of the original image in the list of the calling activity.
How can I do it to make the image return to its position in the list of the calling activity?
The first thing to do is to make sure that the views work properly without any Activity transition. That is, when your return from Activity with the ViewPager, the RecyclerView Activity should be showing the View that the ViewPage was showing. When you call the ViewPager activity, use startActivityForResult and use the result to scroll the RecyclerView to the correct position.
Once that is working, the Activity Transition can be made to work. I know that you've given each View in your RecyclerView a different transitionName, right? When you bind the View, call setTransitionName and give it a repeatable name. Typically this is the image URL or cursor row ID or at worst some munged index like "image_" + index.
The next thing you need to do is set the SharedElementCallback for both the calling Activity (exit) and called activity (enter). In each, you're going to need to override the onMapSharedElements callback to remap the shared element.
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// assuming just one shared element, excuse the ugly code
sharedElements.put(names.get(0), mSharedElement);
}
Here, mSharedElement has been set in the onActivityReenter in the calling (RecyclerView) activity and in onCreate and prior to finishAfterTransition (onBackPressed?) in the called (ViewPager) activity.
The onActivityReenter gives new functionality specifically for this case. You can look at the results there before the called Activity completes.
the title pretty much explains it.
I have horizontal scrolling set up, the first screen has buttons to the other Fragments as well as the whole horizontal scroll system. What I would like is for the user to be able to press the back button when on one of these fragments and for the app to return to the first screen with all the buttons.
From there I want the back button to be an AlertDialog asking the user if they would like to exit the app. At the moment this is what is happening (On all Fragments when you press the back button the AlertDialog I created pops up).
I've looked into Fragment transactions and "addToBackStack()" but I don't know how to implement it. I've looked at the dev guide and certain questions on this site but getting one or two lines of code doesn't help in implementing it.
I have a FragmentActivity with a FragmentPagerAdapter set up and each Fragment has its own Java file. I have 5 Fragments that are all called in the FragmentActivity and FragmentPagerAdapter.
I don't think I need to show you guys any of my code for the moment since it's all set up in the normal manner. Please let me know if you do though.
The bit of code I found on other questions and one in particular was the following:
FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();
It's a bit hard to go on just that though.
I would really appreciate your help.
EDIT: my code removed - wasn't needed.
If you use the ViewPager from your question which I answered earlier and you want to come back to the first fragment of the ViewPager when the user presses the BACK button then override the onBackPressed method like this:
#Override
public void onBackPressed() {
if (getSupportFragmentManager().findFragmentByTag("outDialog") != null
&& ((DialogFragment) getSupportFragmentManager()
.findFragmentByTag("outDialog")).isVisible()) {
// we have the out dialog visible and the user clicked back so let
// the
// normal events happen
super.onBackPressed();
return;
}
int currentPosition = mViewPager.getCurrentItem();
if (currentPosition != 0) {
// if the page the ViewPager shows isn't the first one then move it
// to the first one
mViewPager.setCurrentItem(0);
} else {
// we are at the first position already and the user wants out, so
// annoy him with a dialog that asks him once again if he wants out.
DialogFragment askHim = new DialogFragment();
askHim.show(getSupportFragmentManager(), "outDialog");
// in the dialog listener, if the user presses ok, finish the activity
}
}
did you try to override Activity.onBackPressed() ?
I used addToBackStack() and it works well, but I have no idea whether it works with PagerAdapter.
I think you can override Activity.onBackPressed() and in that method, you can check whether current page is front page or not and do whatever job you want to do.
Here are pseudo code that I think.
public void onBackPressed() {
if( pager.getCurrentPage() == 0 ) { //I'm not sure this method exists or not. just example. :-)
//exit code here
} else {
// show first page
}
}
[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.
There are 4 Tabs in a TabHost, let them be A, B, C, and D. Now each one is just an index page and clicking on any of them shows a different activity.
The problem is that I need to start another activity when the user selects something from the content displayed in the tab. The other activity should also be displayed in the parent tab itself. Is it possible? Or will I have to try something else?
Try this, found this solution in android cookbook,
http://androidcookbook.com/Recipe.seam;jsessionid=5424397F3130CE7769FF47DD67742911?recipeId=1693&recipeFrom=ViewTOC
Can't you change the contentView of your tab instead of starting a new Activity ?
Maybe I'm wrong but I think also that starting an activity in a tab isn't possible because the TabView is hosted in a activity and not the opposite (Tabview don't host an activity per Tab).
I think the common consensus is that it is best not to use individual Activities as tab content due to these limitations. See these questions and answers for pointers to alternatives:
Android: Why shouldn't I use activities inside tabs?
Android - Tabs, MapView, activities within tabs
To summarize the link that Rukmal Dias provided. Here's what you do:
Change your current Activity (that's in a tab) to derive from ActivityGroup
Create a new intent for the Activity you want to switch to
Copy/Paste and call this function in your current activity where "id" is the "android:id" for the layout of the new activity you want to switch to
public void replaceContentView(String id, Intent newIntent){
View view = getLocalActivityManager().startActivity(id,newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) .getDecorView();
this.setContentView(view);}
Here's an example of how I make the call to switch views from my current Tabbed Activity:
public void switchToNextActivity(View view)
{
Intent myIntent = new Intent(getApplicationContext(), MyNextActivity.class);
replaceContentView("next_activity", myIntent);
}
It looses the view hierarchy. When you press the back button, in my case, the app closes.