Warlock
Warlock

Reputation: 2711

How to tag Activity

I'm using this code to jump back in activity stack (mainly to move to home Activity):

Intent goTo = new Intent(this, HomeActivity.class);
goTo.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(goTo);


So I create new Intent and set "target" to HomeActivity which is in Activity stack so whole stack will be cleared from top to this HomeActivity.
Now I need slightly different use case. I have for example five Activities A-B-C-D-E (A started B etc.) on the stack. Now I need to jump from E to C or B depending of what user choose. The problem is that Activities A, B, C, D, E have same class. So I can't use example above because I don't know how to target that Activity.
So the question is if there is any way how to "tag activity" or manipulate with stack.
Thanks!

Upvotes: 9

Views: 6106

Answers (7)

Warlock
Warlock

Reputation: 2711

The best and the easiest solution (so far) will be use Fragments and FragmentManager. Then tag each Fragment and use FragmentManager. Using only Activity can be very difficult to have almost the same result.

Upvotes: 2

Behnam
Behnam

Reputation: 2278

If you want to use an abnormal approach, or some tricky once, you will have more problems later. I think you can

Define a abstract/non-abstract subclass of Activity and define everything you want. If other classes are exactly the same as the above class, so just subclass from it and do nothing more. But if the classes ( Activities ) may different from each other, you can provide abstract/non-abstract methods to define additional abilities.

So

  • You write a reusable codes for all activities,
  • You act normal so you will get good result
  • You can control everything specialized in your activites
  • You can control stack using manifest file
  • and more

for detailed information see below codes:

Parent Activity:

public abstract class AbstractActivity extends Activity {

    AbstractActivity currentActivity;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        currentActivity = this;
        someProtectedMethod();
        commonMethod();
        // etc...

        /* event handling */
        Button btn_first = (Button) findViewById(R.id.btn_first);
        Button btn_second = (Button) findViewById(R.id.btn_second);

        btn_first.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(currentActivity, FirstActivity.class);
                currentActivity.startActivity(intent);
            }
        });

        btn_second.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(currentActivity, SecondActivity.class);
                currentActivity.startActivity(intent);
            }
        });

    }


    /** you must override it, so you can control specified things safe */
    protected abstract void someProtectedMethod();


    protected void commonMethod() {
        Log.i("LOG", "Hello From " + getClass().getName());
    }


    @Override
    protected void onResume() {
        super.onResume();
        //some statement that work in all activities to
        Log.i("LOG", "On Resume: " + getClass().getName());
    }
}

First Activity:

public class FirstActivity extends AbstractActivity {

    @Override
    protected void someProtectedMethod() {
        Log.i("LOG", "Special Action From First Activity");
    }
}

Second Activity:

public class SecondActivity extends AbstractActivity {

    @Override
    protected void someProtectedMethod() {
        Log.i("LOG", "Special Action From Second Activity");
    }
}

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >


    <Button
        android:id="@+id/btn_first"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:text="Open First Activity" />

    <Button
        android:id="@+id/btn_second"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="0.5"
        android:text="Open Second Activity" />

</LinearLayout>

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.activity_control"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="7" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".FirstActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".SecondActivity"
            android:label="@string/app_name" >
        </activity>

    </application>

</manifest>

Upvotes: 2

Sherif elKhatib
Sherif elKhatib

Reputation: 45942

You can index your activities without having to worry about handling all the chain of onActivityResults using a super Activity that you extend in all your activities

Here is an implementation (I did not test it) but if you extend this SuperActivity in all your Activities, you can call fallBackToActivity( int ) to any activity using its index and each activity now has a getIndex(). You can use it to fallback to a relative index like getIndex()-3

package sherif.android.stack.overflow;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class SuperActivity extends Activity {
    private static String EXTRA_INDEX = "SUPER_INDEX";
    private static int RESULT_FALLBACK = 0x123456;
    private int index;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(getIntent()!=null) {
            index = getIntent().getIntExtra(EXTRA_INDEX, -1) + 1;
        }
    }
    protected final int getIndex() {
        return index;
    }
    protected final void fallBackToActivity(int index) {
        Intent intent = new Intent();
        intent.putExtra(EXTRA_INDEX, index);
        setResult(RESULT_FALLBACK, intent);
        finish();
    }
    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        intent.putExtra(EXTRA_INDEX, getIndex());
        super.startActivityForResult(intent, requestCode);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == RESULT_FALLBACK) {
            if(data.getIntExtra(EXTRA_INDEX, -1)!=getIndex()) {
                setResult(RESULT_FALLBACK, data);
                finish();
            }
        }
    }
}

Upvotes: 3

Victor Ronin
Victor Ronin

Reputation: 23278

As I understand Android only targets the class of activity and not a particular instance of activity. So, I think you won't be able to do what you want by just adding some flags on Intent.

I think the easiest approach would be to implement it on your own by something like this

a) Create some singleton and have a member in it which points to instance of activity to which you want to return (as example activity B). Probably, you will have to store all activities in some list to be able to get instance of some previously launched activity.

b) Override onResume for all activities and in it do following check:

if (SomeSingleton.getTargetActivity() != null && this != SomeSingleton.getTargetActivity())
  finish();
else
   SomeSingleton.setTargetActivity(null);

c) As soon as you need to return from E do

SomeSingleton.setTargetActivity(B);
finish();

This will close top activity (which is E) and call onResume on activity D. It will check whether it's target. If it's not then it will close it and system will call onResume on activity C and so on.

Upvotes: 2

Joe
Joe

Reputation: 14059

I haven't tried it myself, but I think the best option would be to refactor your app to use a stack of Fragments within a single Activity (since you can then more easily manage the backstack using the provided addToBackStack() and popBackStack() methods). Basically this involves moving most of the code in your Activity into a Fragment and then adding the backstack manipulation code in the Activity). You can look at the code for FragmentBreadCrumbs (with API 11+) or the code for HanselAndGretel (for use with the compatibility library) to see how this can be implemented.

However, if you want to continue using your current multi-Activity approach, the following is some code I came up with to illustrate how you can do this.

First, add several internal classes to alias your current Activity and put these classes into a sequence list (note also the simplistic getSequencedActivityIntent() method that I wrote, you can add more advanced logic if you need - maybe use a HashMap to associate each class in the sequence with an arbitrary tag value?):

public class MyActivity extends Activity {

    public static class A extends MyActivity {}
    public static class B extends MyActivity {}
    public static class C extends MyActivity {}
    public static class D extends MyActivity {}
    public static class E extends MyActivity {}
    public static class F extends MyActivity {}
    public static class G extends MyActivity {}
    public static class H extends MyActivity {}
    public static class I extends MyActivity {}
    public static class J extends MyActivity {}

    private final static List<Class<?>> SEQUENCE = Arrays.asList(new Class<?>[] {
            A.class, B.class, C.class, D.class, E.class,
            F.class, G.class, H.class, I.class, J.class,
    });

    private Intent getSequencedActivityIntent(int step) {
        final int current = SEQUENCE.indexOf(this.getClass());
        if (current == -1) new Intent(this, SEQUENCE.get(0));

        final int target = current + step;
        if (target < 0 || target > SEQUENCE.size() - 1) return null;

        return new Intent(this, SEQUENCE.get(target));
    }

    // the rest of your activity code
}

Don't forget to add their entries to your AndroidManifest.xml file too (singleTop is optional - it will prevent the Activity instance in the stack to be created again when brought back to the front):

    <activity android:name=".MyActivity$A" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$B" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$C" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$D" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$E" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$F" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$G" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$H" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$I" android:launchMode="singleTop" />
    <activity android:name=".MyActivity$J" android:launchMode="singleTop" />

Now, whenever you need to start a new "top" instance of your Activity, you can do something like:

    final Intent intent = getSequencedActivityIntent(+1);
    if (intent == null) return;
    intent.putExtra("dataset", dataSet);
    startActivity(intent);

And when you need to go back to one of the instance in the backstack you can do:

    final Intent intent = getSequencedActivityIntent(- stepBack);
    if (intent == null) return;
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);

Upvotes: 3

Dimanoid
Dimanoid

Reputation: 7289

Add extra to your intent that will point the activity what to do. For example

    intent.putExtra("STATE", 1);

And get this value in onCreate of your activity.

  getIntent().getExtras()

Upvotes: 2

Pranav Sharma
Pranav Sharma

Reputation: 684

You can just keep a condition in your statement if user chooses this item pass intent to B class and if user chooses that item pass intent to C class

Upvotes: 2

Related Questions