Android ActivityOptions.makeSceneTransitionAnimation doesn't seem to exist - android

Android L introduced a new animations feature: animating between similar Views in different activities. It's documented here.
I've tried to use ActivityOptions.makeSceneTransitionAnimation, but it doesn't seem to be visible in the SDK (or in the jar at all), so I tried using reflection, and it returns a null value.
Has anyone else got it working?

Okay, I got it working.
It seems like setting the value in styles.xml is completely ignored for now.
You'll need to do this in each Activity's onCreate till that's fixed
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
Transition transition = // load transition here.
getWindow().setSharedElementEnterTransition(transition);
getWindow().setSharedElementExitTransition(transition);
As per the same bug ViewAnimationUtils has, you'll see errors in Android Studio, it'll compile and run fine though.

We can got it working with theme config for v21.
Put these items into res/values-v21/styles.xml
<item name="android:windowContentTransitions">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>

Here is something work with 5.0 sdk got after 10/17/14.
But not sure what is the expected behavior if enable window content transitions and called setEnterTransition/setExitTransition in both mainActivity and secondActivity. If they are different (e.g one choose Explode and other choose Slide), which one would be applied?
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
To enable window content transitions in your code instead, call the Window.requestFeature() method:
*/
getWindow().requestFeature(android.view.Window.FEATURE_CONTENT_TRANSITIONS);
Transition ts = new Explode(); //Slide(); //Explode();
ts.setStartDelay(2000);
ts.setDuration(5000);
/*
If you have set an enter transition for the second activity,
the transition is also activated when the activity starts.
*/
getWindow().setEnterTransition(ts);
getWindow().setExitTransition(ts);
setContentView(R.layout.activity_main_view);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, mainViewFragment.newInstance())
.commit();
}
}
public void launchSecondActivity() {
/*
If you enable transitions and set an exit transition for an activity,
the transition is activated when you launch another activity as follows:
*/
Intent listIntent = new Intent(this, secondActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
}
}
//===
public class secondActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
///
getWindow().requestFeature(android.view.Window.FEATURE_CONTENT_TRANSITIONS);
Transition ts = new Slide(); //Slide(); //Explode();
ts.setDuration(3000);
getWindow().setEnterTransition(ts);
getWindow().setExitTransition(ts);
///
setContentView(R.layout.activity_scene_transition);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}

Related

Floating action button blink after closing activity using Shared Element Transition

I have an issue with Shared Element Transition.
When I return to MainActivity from DetailActivity, FAB blinking
Gif example
I used this sample project.
For shared element transition I made:
Enabled Window Content Transitions in styles.xml
<item name="android:windowContentTransitions">true</item>
Assign a common transition name to the shared elements in both layouts.
android:transitionName="image"
Started the target activity by specifying a bundle of those shared elements and views from the source
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, CheeseDetailActivity.class);
intent.putExtra(CheeseDetailActivity.EXTRA_NAME, holder.mBoundString);
MainActivity activity = (MainActivity) context;
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, new Pair<>(holder.mView.findViewById(R.id.avatar), "image"));
ActivityCompat.startActivity(context,intent, options.toBundle());
}
});
And when i press back button, FAB from detail Activity blinks in Main activity.
I did not find a similar problem, so thanks for any help!
I fixed it by hiding FAB before close Activity.
In onBackPressed() and in home button onClick i pasted:
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) myFab.getLayoutParams();
params.setBehavior(null);
myFab.requestLayout();
myFab.setVisibility(View.GONE);
Maybe it will be useful for someone.

Text Of EditTextPreference Turns White On Orientation Change

I'm using SettingsActivity as preference activity for my application like so:
public class SettingsActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.dark_theme_key), false)) {
setTheme(R.style.AppThemeDark);
} else {
setTheme(R.style.AppThemeLight);
}
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}
public static class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
...
}
}
}
If I use getActivity().recreate(); (I have a SwitchPreference for dark and light theme. Dark theme is Theme.AppCompat and light is same but with .Light. If I change the theme I use recreate.) or change orientation of the screen (Resulting in onCreate of course), I get a weird problem when I'm on my light theme.
The problem is as follows -after a recreate, the text content of the EditTextPreference's turn white instead of black.
If I simply ignore savedInstanceState == null and just create a new fragment every time on SettingsActivity.onCreate, I lose the "screen state" (for example if I was scrolled all the way down before, after recreate Im scrolled back to the top. If I had a EditTextPreference open before, after recreate its closed).
I'm not sure how to restore a fragment screen state in case of recreation, or why my problem even occurs. Would appreciate help.

IllegalStateException fragment did not create a view after orientation change

I have an app that has a main activity that the user can select an item from. That selection brings up a fragment (TracksActivityFragment) that itself is another list. When an item of that list is selected, a fragment is added that is a DialogFragment. So far so good, but when I rotate the device, the AFragment's onCreate() gets called and then the DailogFragment's onCreate() gets called, then it dies with the IllegalStateException saying that it dies on AFragment's Activity line 20 (setContentView).
Here is a part of that Activity with the line in question:
public class TracksActivity extends AppCompatActivity
{
private String mArtistName;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tracks); //DIES HERE
Here is the onCreate of the fragment
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if(savedInstanceState != null)
{
//We've got data saved. Reconstitute it
mTrackRowItemList = savedInstanceState.getParcelableArrayList(KEY_ITEMS_LIST);
}
}
The DialogFragment gets created in the TracksFragment like this:
PlayerFragment fragment = PlayerFragment.newInstance(mTrackRowItemList, i, mArtistBitmapFilename);
// The device is smaller, so show the fragment fullscreen
FragmentTransaction transaction = fragMan.beginTransaction();
// For a little polish, specify a transition animation
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// To make it fullscreen, use the 'content' root view as the container
// for the fragment, which is always the root view for the activity
transaction.add(android.R.id.content, fragment).addToBackStack(null).commit();
Here is the DialogFragment's onCreate
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
//We've got data saved. Reconstitute it
if (mPlayer != null)
{
mPlayer.seekTo(savedInstanceState.getInt(KEY_SONG_POSITION));
}
}
}
Not sure why it goes back to the TracksFragment since it had the DialogFragment active on rotation, but since that is the case, it would seem like I would need to recreate the entireDialogPlayer object, But it seems to keep this around as the call to its onCreate happens.
Anyone know what it is that needs to be done here?
OK, this was asked before but I discounted the solution...I should not have.
For some reason, Android wants the Tracks layout XML to use a FrameLayout instead of a fragment.
So, just replace fragment with FrameLayout in the layout xml file and all is well.

Android up button with Viewpager: go back to the previously selected fragment

I'm having some problems with navigation and the up button of the activity. I have two tabs implemented with Viewpager in one activity and then another activity which is loaded from the previous one. When the user clicks the phone's back button or the activity's up button, I want to go back to the previously selected tab and fragment of the first activity.
So far I've been able to do it for the back button, with onSaveInstanceState and the following code:
#Override
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putInt("currentPage", mViewPager.getCurrentItem());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
int page = savedInstanceState.getInt("currentPage", 0);
mViewPager.setCurrentItem(page);
getSupportActionBar().setSelectedNavigationItem(page);
}
}
Why is this not working for the up button? What do I have to do?
Any help is appreciated and sorry if this is an issue easy to solve, but I'm rather new to the Android programming.
I finally found the solution at Returning from an activity using navigateUpFromSameTask(). savedInstanceState was null when the activity was recreated. To avoid so, the launch mode of the activity has to be declared as singleTop in the Android manifest.

When to call openDrawer() (or any other animation) to make it visible on startup?

I want my app to show the side navigation drawer as soon as the main activity is created.
My code works fine - user launches app and gets the open drawer - but I'd like to actually see the side drawer sliding from the left; instead, I find the drawer fully opened.
At what point should I call openDrawer()?
Have tried calling from:
main activity OnCreate;
similar points in the fragment hosted by the drawer.
I could try OnPrepareOptionsMenu, but I think it gets called more than once during the activity lifecycle. I also tried OnStart() and I fear my options are over.
Any idea? I'm sure this is pretty simple but I can't figure out.
Edit: I realize I wasn't so clear with my first exposition of the question (#Biu). I'm talking about a purely graphical issue here. The point is:
I have something to happen at startup; in my case we're speaking about the nav drawer sliding into the main screen, but it could be any animation I think;
In my case, one could just call:
protected void OnCreate(Bundle b) {
...
DrawerLayout.openDrawer()
}
The above solution works well. The issue I'm talking about is graphical; with the above code you launch the app and find the main activity covered with an already-opened drawer. Instead, I'd like the user to have clue of what is happing, to see where the panel came from; in other words, to see the opening animation.
So my question is: when should I call openDrawer()? The main Activity onCreate isn't quite right, because the animation ends before the user gets to see something on screen.
I thought that the wish of having something start when all is loaded would be more common.
#benjosantony suggests that you should open your drawer at onResume, however it's not guaranteed that the activity will be visible at that time:
onResume is not the best indicator that your
activity is visible to the user; Use onWindowFocusChanged(boolean) to know for certain
that your activity is visible to the user
You'd think that you can just use onWindowFocusChanged and be done, but you can't. There's still the transition animation which breaks (at least for me) the drawer's animation..
For API 21+:
There's onEnterAnimationComplete where you can open your drawer and see the animation properly. However 21+ is a requirement that's just too big..
For lower APIs:
The only possible way I can think of is removing the activity's animation with a theme adjustment:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowAnimationStyle">#null</item>
</style>
And opening the drawer like so:
private static final String DRAWER_STATE = "mDrawerOpened";
private DrawerLayout mDrawer;
private ListView mDrawerList;
private boolean mDrawerOpened;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!mDrawerOpened && hasFocus) {
mDrawer.openDrawer(mDrawerList);
mDrawerOpened = true;
}
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(DRAWER_STATE, mDrawerOpened);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mDrawerOpened = savedInstanceState.getBoolean(DRAWER_STATE);
}
This will animate the drawer only when the activity is started.
The boolean value is saved when your activity is destroyed abnormally, e.g. rotation or need for system resources.
If you don't like setting the instanceState you can use SharedPreferences as #Biu suggested, however IMO that wouldn't be the proper solution as android already provides tools for that, there's no need to re-invent the bike.
You could use this hack by using SharedPreferences
boolean firstTime = true;
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
public void onCreate(Bundle bundle)
{
if (pref.getBoolean("firstTime", true) == true)
{
drawer.openDrawer(yourDrawer);
firstTime = false;
pref.editor().putBoolean("firstTime", firstTime).apply();
}
}
Activity
The foreground lifetime of an activity happens between a call to onResume() until a corresponding call to onPause(). During this time the activity is in front of all other activities and interacting with the user.
Thus I think onResume is the best place to open your drawer.

Categories

Resources