I have a use case where I mostly start an activity with a transition, but that's not the case when opening it from the navigation drawer.
To keep the transition smooth I have a Transition.TransitionListener in which I trigger some UI updating when the transition is done.
So I have something like this:
public class SomeActivity extends Activity {
public void onCreate(Bundle savedInstanceState){
// ...
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
Transition sharedElementEnterTransition = getWindow().getSharedElementEnterTransition();
sharedElementEnterTransition.addListener(new Transition.TransitionListener() {
// ...
#Override
public void onTransitionEnd(Transition transition) {
doSomeUiUpdating();
}
});
} else { // Pre-Lollipop
doSomeUiUpdating();
}
}
}
This works well when starting the Activity with the animation, but how can I know if the Activity was started without a transition so that I can call doSomeUiUpdating()?
I'm sure there must be a simple method in Activity, Window, Transition or somewhere that I have overlooked. I don't want to relay on the calling Activity to set some bundle that telling if the animation is showing or not.
You can try onTransitionStart of TransitionListener to set some boolean isAnimationStarted.
public class SomeActivity extends Activity {
private boolean isAnimationStarted = false;
public void onCreate(Bundle savedInstanceState) {
// ...
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
Transition sharedElementEnterTransition = getWindow().getSharedElementEnterTransition();
sharedElementEnterTransition.addListener(new Transition.TransitionListener() {
// ...
#Override
public void onTransitionEnd(Transition transition) {
doSomeUiUpdating();
}
#Override
public void onTransitionStarted(Transition transition) {
isAnimationStarted = true;
}
});
}
}
public void onStart() {
if (!isAnimationStarted) {
doSomeUiUpdating();
}
}
}
Since you are starting an Activity, you'll be making use of an Intent to start it. You can add extras to Intents and check for them in the onCreate() of the called Activity.
Let's assume that we have 2 Activities – ActivityA, and ActivityB.
Now, let's assume that ActivityA is the calling Activity, and that ActivityB is the called Activity.
In ActivityA, let's say you've written some code to start ActivityB with a SharedElementTransition.
#Override
public void onClick(View v) {
Intent startActivityBIntent = new Intent(getContext(), ActivityB.class);
startActivityBIntent.putExtra("IS_SHARED_ELEMENT_TRANSITION_ENABLED", true);
ActivityOptionsCompat activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), view, ViewCompat.getTransitionName(view));
startActivity(startActivityBIntent, activityOptionsCompat);
}
Now, if you notice above, I've passed an Intent extra with the putExtra() method. I've passed a Boolean value of true because I intend to start the Activity with a SharedElementTransition.
Now in ActivityB's onCreate() method, you can just check for the boolean value passed to the Intent. If you passed false, then you can add a conditional statement and perform your UI updating there. I've given you a small snippet below to help you get started:
private static final String isSharedElementTransitionEnabled = "IS_SHARED_ELEMENT_TRANSITION_ENABLED";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
// If you are postponing your SharedElementTransition, don't forget to call postponeEnterTransition() and override onPreDraw()
if (!getIntent().getExtras().getBoolean(isSharedElementTransitionEnabled)) {
//Do your UI updation here
}
}
The good thing about doing it this way is that you can then have full control over how your content transitions and your shared element transitions will play out.
Related
I have my code defined the way below. There are two crucial activities. Activity (1) shows some images in a ViewFlipper. It uses methods to load desired image directly. The onOptionsItemSelected() method fetches data from a menu defined within linked XML layout R.layout.browse. The other method, displaySelectedFlag(), gets a tag parameter passed from a different activity, let's call it activity (2).
Activity (1):
public class BrowserActivity extends AppCompatActivity implements SimpleGestureListener, View.OnClickListener {
public ViewFlipper vFlipper;
(...)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browse);
vFlipper = (ViewFlipper) findViewById(R.id.viewFlipperBrowser);
(...)
} // onCreate() ends here
// this method below works fine:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
vFlipper.setDisplayedChild(item.getOrder());
return true;
}
// and this one doesn't:
public void displaySelectedFlag(int orderTag) {
vFlipper.setDisplayedChild(orderTag); // crashes here
}
}
Activity (2):
public class ListActivity extends Activity implements View.OnClickListener {
private BrowserActivity browserActivity = new BrowserActivity();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
ImageButton imageA = (ImageButton) findViewById(R.id.img_a);
imageA.setOnClickListener(this);
ImageButton imageB = (ImageButton) findViewById(R.id.img_b);
imageB.setOnClickListener(this);
}
public void displayImageInfo(View view) {
String tagValue = (String) view.getTag();
int tagId = Integer.parseInt(tagValue);
Intent intent = new Intent(this, BrowserActivity.class);
startActivity(intent);
browserActivity.displaySelectedImage(imageId);
}
#Override
public void onClick(View view) {
displayImageInfo(view);
}
}
As I checked, the method onClick() called in activity (2) fetches an ID of an ImageButton and passes it to activity (1). Unfortunately, I get a NullPointerException when calling the ViewFlipper (the line is marked in the code above, activity (1)).
Any idea why it happens?
You cannot reference one Activity from another activity. You must let the Android OS create the Activity object via the call to "startActivity". Allocating a local variable as an instance of an Activity doesn't actually mean anything (like your instantiation of the BrowserActivity). Apoorv's comment links to a decent article on the subject.
If you want to pass data from one Activity to another, you need to pass extras within the Intent's bundle. This post goes into detail: https://stackoverflow.com/a/819427/504252
I want to call foo() every time my activity is destroyed, unless it is destroyed as result of clicking on a specific menu option (that eventually calls finish()). Currently I do this by calling foo() on default in onDestroy, unless a FLAG is set to true, where FLAG is set when I intercept the click on the menu option.
Is there a better way of doing this than setting a flag? Perhaps some way I can attach a tag to Android's finish() so that I can see the reason? Normally I would just try to call foo where it applies, but I can't account for every reason an activity might be destroyed.
Further caveat is that I would prefer not to make changes to base class (RootActivity)
public abstract class RootActivity extends Activity{
private flag someCondition;
#Override
protected void onCreate(Bundle savedInstanceState){
//...
}
// ...
public void startJob(JobAction.Id jobaction){
if (!jobaction.someCondition){
return;
}else{
startSomeLongAsynchronousJob(someCondition);
finish(); //If this is why onDestroy happened in subclass, I dont want to call foo()
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
//...
startJob(JobAction.SOMEENUM); //Startjob is being called in the superclass
}
//...
}
public class SpecificJob extends SomeClassThatExtendsRoot{
private boolean FLAG = false;
#Override
public void onCreate(Bundle bundle){
super.onCreate(bundle);
//...
}
// ... some code ...
#Override
public onDestroy(){
if (!FLAG){ //Check if it was finish() that did this
foo();
}
super.onDestroy();
}
#Override
onOptionsItemSelected(MenuItem item){
super.onOptionsItemSelected(item);
if (item.getItemId()==r.id.DONTCALLFOO){
flag=true;
}
}
}
You have to override onSaveInstanceState in your activity.
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.d("ApplicationFlow","onSaveInstanceState was called. System destroy your activity");
foo();
super.onSaveInstanceState(outState);
}
It is always called when the activity is destroyed by the system, and not by you (when you call finish()).
The purpose is give to user a chance to save some state in Bundle outState parameter. This bundle will be passed to onCreate(Bundle savedInstanceState) to the user restore the state, when the activity is going to be recreated.
See documentation
I have an Activity class and inside there are some methods. And I want to implement the onBackPressed() inside the method2 because I have an important variable that I want to free. I can't/don't make this variable with bigger scope and I can't free this variable inside the method2 because I want to terminate the application and the execution of method 2 with the pressing back button.
public class example extends Activity {
public void onCreate(Bundle savedInstanceState) {
method1();
}
public method 1 {
//take some input and assign in a variable.
method2(variable);
}
public method2 {
// do something with the variable that take before at method 1
// and finally press back button
onBackPressed(){}
//free variable , finish ();
}
}
As you know i can't Override the onBackPressed() inside the method only out at the activity area. Can you provide me a solution for this.
You should override the onBackPressed() method in the activity scope and call it from your method.
#Override
public void onBackPressed()
{
super.onBackPressed();
// Do your things.
}
public void method()
{
onBackPressed();
}
If you want to add some complex logic in the onBackPressed method, just create another one with parameters.
public void myOnBackPressed(int param1, String param2)
{
// Do your complex logic.
onBackPressed();
}
public void method()
{
myOnBackPressed(myInt, myString);
}
I have an app where I used default slide in/out effects on standards activities and fade in/out on a few specific activities.
I've noticed that the new Android 4.X (ICS) has a different default, which is exactly fade in/out.
How can i define on my standard activities my slide in/out effect?
I've been trying to define it with:
#Override
public void onCreate(Bundle savedInstanceState)
{
this.overridePendingTransition(0, android.R.anim.slide_in_left);
super.onCreate(savedInstanceState);
}
#Override
public void onPause()
{
super.onPause();
overridePendingTransition(android.R.anim.slide_out_right, 0);
}
To reproduce the fade in / out effect i'm using:
#Override
public void onCreate(Bundle savedInstanceState)
{
this.overridePendingTransition(0, android.R.anim.fade_in);
super.onCreate(savedInstanceState);
}
#Override
public void onPause()
{
super.onPause();
overridePendingTransition(android.R.anim.fade_in, 0);
}
but haven't managed to successfully reproduce it.
Also, I'm using this code on onCreate and onPause, is this correct? Or is there a better place to put it?
Thanks
PS: I've seen this New Android 4.0 Screen Transitions Between Activities but now answer has been provided.
overridePendingTransition() has to be called immediately after starting the new activity, so you would call it after startActivity(intent), for example.
To make a default you can create a class that extends Activity and override startActivity, onBackPressed(), etc. to override the transition. For example:
public class MyActivity extends Activity {
#Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
#Override
public void startActivity(Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
#Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.slide_in_transition,R.anim.slide_out_transition);
}
}
Then, just extend MyActivity instead of Activity in your activities and the transitions should all be sliding by default.
I have a main activity that hold the tabs and each tab start a new activity. May I know how can I change the tab title from the new activity? Thanks.
Although CommonsWare has pointed out that having Activities as Tab content is deprecated, if you still want to do it then one possibility is to use a nested BroadcastReceiver and have the content Activity send a broadcast Intent. I'm not sure if it will work but I would try something like the following...
public class MainActivity extends Activity {
bool tabMonitorIsRegistered = false;
TabMonitor tabMonitor = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code
tabMonitor = new TabMonitor();
}
#Override
protected void onResume() {
super.onResume();
if (!tabMonitorIsRegistered) {
registerReceiver(tabMonitor, new IntentFilter(Intent.com.mydomain.myapp.ACTION_TAB_CHANGE));
tabMonitorIsRegistered = true;
}
}
#Override
protected void onPause() {
super.onPause();
if (tabMonitorIsRegistered) {
unregisterReceiver(tabMonitor);
tabMonitorIsRegistered = false;
}
}
// Nested BroadcastReceiver
private class TabMonitor extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// Process the Intent here to change the tab title
}
}
}
At this point it occurs to me that each 'content' Activity will need to tell the MainActivity (via the Intent it sends) 'who' it is. To do this, I would use an Intent extra when adding the tab content Activities identifying each as 'tab1', tab2' etc. When the 'content' Activities start, e.g., in onCreate(), they can store this string and use it in the Intent they send as the broadcast to the MainActivity.