简体   繁体   中英

newInstance() with custom Java Generic?

I am working on my first 'Non-tutorial' application to grow and strengthen my Android development skill sets.

I've been using lots of Java Generics to enhance reusability and debugging, especially since lots of my fragments do the same thing with subclasses of a Question class.

I just ran across a conventional pattern that is new to me and was wondering if I can apply it to Generic Classes in Java.

According to the text, a newInstance(args,...) method should be created within Fragment classes to handle the transition of Intent Extras to Fragment Arguments .

Example:

SomeActivity.class

@​O​v​e​r​r​i​d​e​
p​r​o​t​e​c​t​e​d​ ​F​r​a​g​m​e​n​t​ ​c​r​e​a​t​e​F​r​a​g​m​e​n​t​(​)​ ​{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​e​w​ ​ObjectF​r​a​g​m​e​n​t​(​)​;​

 ​ ​ ​ ​U​U​I​D​ ​object​I​d​ ​=​ ​(​U​U​I​D​)​g​e​t​I​n​t​e​n​t​(​)​
 ​ ​ ​ ​ ​ ​ ​ ​.​g​e​t​S​e​r​i​a​l​i​z​a​b​l​e​E​x​t​r​a​(Object​F​r​a​g​m​e​n​t​.​E​X​T​R​A​_​OBJECT_​I​D​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​Object​F​r​a​g​m​e​n​t​.​n​e​w​I​n​s​t​a​n​c​e​(object​I​d​)​;​
}​

ObjectFragment.class

p​u​b​l​i​c​ ​s​t​a​t​i​c​ ObjectF​r​a​g​m​e​n​t​ ​n​e​w​I​n​s​t​a​n​c​e​(​U​U​I​D​ ​object​I​d​)​ ​{​
 ​ ​ ​ ​B​u​n​d​l​e​ ​a​r​g​s​ ​=​ ​n​e​w​ ​B​u​n​d​l​e​(​)​;​
 ​ ​ ​ ​a​r​g​s​.​p​u​t​S​e​r​i​a​l​i​z​a​b​l​e​(​E​X​T​R​A​_​C​R​I​M​E​_​I​D​,​ ​object​I​d​)​;​

 ​ ​ ​ ​ObjectF​r​a​g​m​e​n​t​ ​f​r​a​g​m​e​n​t​ ​=​ ​n​e​w​ ​ObjectF​r​a​g​m​e​n​t​(​)​;​
 ​ ​ ​ ​f​r​a​g​m​e​n​t​.​s​e​t​A​r​g​u​m​e​n​t​s​(​a​r​g​s​)​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​f​r​a​g​m​e​n​t​;​
}​

Excerpt From: Brian Hardy. “Android Programming: The Big Nerd Ranch Guide.”

But what about cases using Java Generics?

Code I am working on:

QuestionListActivity.class

public class QuestionListActivity extends SingleFragmentActivity {


    // CONSTANTS
    public static final String EXTRA_FRAGMENT_TYPE = "com.renaissanceartsmedia.flashcard.editquestionactivity.fragment";
    public static final String EXTRA_ACTIVITY_TITLE = "ListQuestionActivity.EXTRA_ACTIVITY_TITLE";
    public static final String TAG = "QuestionListActivity";

    // Member Properties
    QuestionType mFragmentType;

    @Override
    protected Fragment createFragment() {

        mFragmentType = (QuestionType) getIntent().getSerializableExtra(EXTRA_FRAGMENT_TYPE);
        System.out.println("mFragmentType: " + mFragmentType);

        // Switch on Enumeration
        switch (mFragmentType) {

            case MULTIPLE_ANSWER_QUESTION:
            case MULTIPLE_CHOICE_QUESTION:
            case TRUE_FALSE_QUESTION:

                // PREVIOUS METHOD
                //return new QuestionListFragment<MultipleAnswerQuestion>();

                // Attempting to refactor to newInstance(Bundle args)
                return QuestionListFragment<MultipleAnswerQuestion>.newInstance(getIntent().getExtras()); // ERROR

            case MATCHING_QUESTION:
                return new QuestionListFragment<MatchingQuestion>();

            case BLANK_QUESTION:
                //return new BQFragment();
                return new QuestionListFragment<BlankQuestion>();
            default:
                return new QuestionListFragment<Question>();
        }

    }
}

Currently, I am getting the Extras from within the QuestionListFragment's onCreate() method. I know that I will remove this code if transitioning to the newInstance() convention should be used with Java Generics.

QuestionListFragment.class

    public class QuestionListFragment<E extends Question> extends ListFragment implements QuestionDialogInterface {

        // Constants
        public static final String TAG = "QuestionListFragement";
        public static final String DIALOG_TITLE = "QuestionListFragment.DIALOG_TITLE";
        public static final String DIALOG_MESSAGE = "QuestionListFragment.DIALOG_MESSAGE";
        public static final String QUESTION_TYPE = "QUESTION_TYPE";
        private static final int DIALOG_FRAGMENT = 1;

        // Member Properties
        Flashcard mFlashcard;
        QuestionType mQuestionType;
        String mActivityTitle;
        ArrayList<E> mQuestions;
        DialogFragment mDialogFragment;

// SOMETHING LIKE THIS???
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public static QuestionListFragment<? extends Question> newInstance(Bundle args) {
            // Create a new instance of QuestionListFragment<? extends Question>
            QuestionListFragment<? extends Question> fragment = new QuestionListFragment();

            // Set the arguments
            fragment.setArguments(args);

            // Return the Fragment
            return fragment;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(TAG,"Enter onCreate(Bundle savedInstanceState)");
            // Enable Options Menu
            setHasOptionsMenu(true);

            // Create the ActionBar 'UP' Button
            getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);

            // The Intent Extras
            Bundle extras = getActivity().getIntent().getExtras();

            // Extract the Flashcard from the extras
            UUID flashcardId = (UUID) extras.getSerializable(Flashcard.EXTRA_FLASHCARD_ID);
            mFlashcard = FlashcardStore.get(getActivity()).getFlashcard(flashcardId);
            mQuestionType = (QuestionType) extras.getSerializable(EditQuestionActivity.EXTRA_FRAGMENT_TYPE);
            mActivityTitle = extras.getString(QuestionListActivity.EXTRA_ACTIVITY_TITLE);


            // Get a Container of Multiple Answer Questions
            mQuestions = (ArrayList<E>) mFlashcard.getQuestions(mQuestionType);

            // Set the Title of the Fragment's Activity
            getActivity().setTitle(mActivityTitle);

            // Create a list 
            ListItemLayoutAdapter adapter = new ListItemLayoutAdapter(mQuestions);

            // Set the adapter for the list
            setListAdapter(adapter);
            Log.d(TAG,"Exit onCreate(Bundle savedInstanceState)");
        }
    ....
    }

What are Best Practices in regards to Android Fragments and Java Generics? Can someone describe what they are, why they should be used. If newInstance() should be used, please help me fix the error by providing the correct syntax for declaring:

// Attempting to refactor to newInstance(Bundle args)
 return QuestionListFragment<MultipleAnswerQuestion>.newInstance(getIntent().getExtras()); // ERROR
    public static <T extends Question> QuestionListFragment<T> newInstance(Bundle args) {
        // Create a new instance of QuestionListFragment<? extends Question>
        QuestionListFragment<T> fragment = new QuestionListFragment<T>();

        // Set the arguments
        fragment.setArguments(args);

        // Return the Fragment
        return fragment;
    }

and then to call it:

    QuestionListFragment.<MultipleAnswerQuestion>newInstance(getIntent().getExtras()‌​);
 public static <T extends BaseFragment> T getInstance (Class<T> mClass, Bundle args) {
    try {
        T instance = mClass.newInstance();
        instance.setArguments(args);
        return instance;
    } catch (java.lang.InstantiationException | IllegalAccessException e) {
        /**
         * Error thrown
         */
    }

    return null;
}

A shorter version. Cheers

If your purpose is to extend an "BaseFragment" with some custom behavior already implement to share it between the Childs, here is how I does it:

Create the BaseFragment with a generic method for instante childs.

public abstract class BaseFragment extends Fragment {

    public static <T extends NewReportFragment> T newInstance(Delegate delegate, Class<T> classInstance) {
        T fragment = null;
        try {
            fragment = classInstance.newInstance();
            fragment.setDelegate(delegate);
        } catch (java.lang.InstantiationException e) {
            e.printStackTrace();
        } catch (java.lang.IllegalAccessException e) {
            e.printStackTrace();
        }
        return fragment;
    }
}

And you can instantiate the childs as this:

FirstFragment aFragment = BaseFragment.newInstance(this, FirstFragment.class);

Childs of BaseFragment must contain a default constructor to can be instantiated generically.

public static class ConfirmationFragment extends NewReportFragment {
        public ConfirmationFragment() { }
}

NOTE: Delegate is just an interface to interact with the fragments generically from the activity. If you need to know about delegates, take a look to this .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM