Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

launch fragment by variable name

I was wondering if it was possible to launch a fragment by variable name rather then hard coding the fragments name.

Allow me to post a sample

This is how you traditionally launch a fragment:

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.your_placehodler, new YourFragment());
ft.commit();

But say you are trying to launch the fragment without knowing the name of it, or possibly which fragment it is. Say like a listFragment, or a Listview and you are running through an array of Fragment names. Hence you would do something like this:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {

     private String[] values = new String[] { "frag1", "frag2", "frag3" };

     String someFragment = values[position];
     String fragName = (someFragment + ".class");

     try {
     FragmentTransaction ft = getFragmentManager().beginTransaction();
     ft.replace(R.id.your_placehodler, new fragName());
     ft.commit();
     } catch (Exception e) {
     //print message
     }

I know this is not correct, but I feel like if it's possible I may be close. I searched for a while but I found nothing.

So my question, Is this possible? If so how would I implement it? Thanks!

Edit I attempted what I thought may work with the Reflections API using this code

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String questions = values[position];

    try {
        Fragment frags = (Fragment) Class.forName("com.example.android." + questions).newInstance();            
        getFragmentManager()
                .beginTransaction()
                .setCustomAnimations(android.R.animator.fade_in,
                        android.R.animator.fade_out)
                .replace(R.id.header_fragment_container, frags).commit();

    }

    catch (Exception e) {

    }

}
}

I get a message saying 05-08 04:38:14.124: W/dalvikvm(812): dvmFindClassByName rejecting 'com.android.example.Ovens'

Yet if in my code I change the line to say Fragment frags = (Fragment) Class.forName("com.android.example." + "Ovens").newInstance();

It works

The variable "questions" is an exact copy of the class name. I don't see why it wouldn't work. Nothing happens, nothing prints to the logcat

Final Edit

Got it! I was missing the "" marker. Here is the final working code, thanks for all the help

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String questions = values[position];
    try {
        Fragment frags = (Fragment) Class.forName("com.android.example." + "" + questions).newInstance();
        getFragmentManager()
                .beginTransaction()
                .setCustomAnimations(android.R.animator.fade_in,
                        android.R.animator.fade_out)
                .replace(R.id.header_fragment_container, frags).commit();
    }

    catch (Exception e) {

    }
}
}
like image 782
IrishWhiskey Avatar asked May 08 '13 00:05

IrishWhiskey


2 Answers

Probably too late to answer but putting here for others. Instead of using

 Fragment frags = (Fragment) Class.forName("com.android.example." + "" + questions).newInstance();

use

Fragment frags = Fragment.instantiate(mContext,"com.android.example." + "" + questions);

and if you want to pass arguments to the fragment you can use

Fragment frags = Fragment.instantiate(mContext,"com.android.example." + "" + questions,bundle);

where bundle is the Bundle carrying your data.

Edit:

As to why you should prefer this over other:

  1. You can send Bundle args to you fragment
  2. caching using sClassMap
  3. Sanity checks for whether the class initialized is fragment or not

Here is the code for instantiate method in fragment

public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
        try {
            Class<?> clazz = sClassMap.get(fname);
            if (clazz == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = context.getClassLoader().loadClass(fname);
                if (!Fragment.class.isAssignableFrom(clazz)) {
                    throw new InstantiationException("Trying to instantiate a class " + fname
                            + " that is not a Fragment", new ClassCastException());
                }
                sClassMap.put(fname, clazz);
            }
            Fragment f = (Fragment)clazz.newInstance();
            if (args != null) {
                args.setClassLoader(f.getClass().getClassLoader());
                f.mArguments = args;
            }
            return f;
        } catch (ClassNotFoundException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (java.lang.InstantiationException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (IllegalAccessException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        }
    }

where sClassMap is a HashMap

private static final HashMap<String, Class<?>> sClassMap =
        new HashMap<String, Class<?>>();

obviously you can implement the same feature in above code. Reason not to do so "DRY"

like image 189
Anirudha Agashe Avatar answered Oct 29 '22 08:10

Anirudha Agashe


One way to acheive this is through the reflection APIs.

Class.forName("com.example.MyFragment").newInstance();

This can throw a whole host of Exceptions so look out for that.

Another way would be to create a simple factory class for your Fragments.

public abstract class MyFragmentFactory {
    private MyFragmentFactory(){}

    public static <T extends Fragment> T getFragment(String name){
        if("MyFragment".equals(name)){
            return new MyFragment();
        }else if("whatever".equals(name)){
            // ...
        }else{
            throw new RuntimeException("unknown fragment "+ name);
        }
    }
}
like image 35
alex Avatar answered Oct 29 '22 08:10

alex