Android Fragment Learning and Use - Advanced Chapter

Keywords: Fragment RabbitMQ Hadoop Spark

I. overview

The last article has explained Fragment's life cycle, basic usage methods and the role of some api s. But if you want to use Fragment well in your project, you must be able to clearly manage its status. Here are some scenarios that you will encounter in actual development.

2. Fragment Back Stack Management

Activity is managed by task stack. Following the principle of first-in-last-out, Fragment can also implement similar stack management, so that multiple Fragments can be added successively and then returned to the previous Fragment. When there is no Fragment in the activity container, it exits Activity.

Specific method: FragmentTransaction.addToBackStack(String) // usually passed into null

The code is as follows:

Fragment f = new Fragment();  
FragmentManager fm = getSupportFragmentManager();  
FragmentTransaction ftx = fm.beginTransaction();  
ftx.replace(R.id.fragment_container, f, "ONE");  
ftx.addToBackStack(null);  
ftx.commit();

Note:
1. The first Fragment (root Fragment) of an activity can exit the activity without adding a fallback stack, so that the last Fragment will not be blank when it returns.
2. Calling addToBackStack(null) adds the current transaction to the fallback stack. The Fragment instance will not be destroyed after calling the replace method, but the view level will be destroyed, that is, onDestoryView and onCreateView will be called. If you need to save the current fragment view state, you can add a new fragment after hide

3. Fragment and Activity Communication

a. If Activity contains references to Fragments managed by itself, you can access all Fragments'public methods directly by reference
b. If no reference to Fragment is saved in Activity, any Fragment instance can be obtained by using getFragmentManager.findFragmentByTag() or findFragmentById() with a unique TAG or ID for each Fragment and then operated on.
c. In Fragment, you can get an instance of the currently bound Activity through getActivity, and then operate on it.

Note: If you need Context in Fragment, you can call getActivity(), and if the Context needs to exist after the Activity is destroyed, use getActivity().getApplicationContext().

Recommendation:
1. Interface (Fragment returns data to Activity)

Fragment part code:

public class TestFragment extends Fragment {

    private OnSaveListener listener;

    public void setListener(OnSaveListener listener) {
        this.listener = listener;
    }

    public interface OnSaveListener {
        void onSaveFinished(boolean result);

        void onSaveStart();
    }


    @OnClick(R.id.btn_save)
    public void save() {
        ....
        listener.onSaveFinished(true);
    }
}

Activity part code:

TestFragment f = new TestFragment();
f.setListener(new ShowCheckFragment.OnSaveListener() {

    @Override
    public void onSaveFinished(boolean result) {
        ......
    }

    @Override
    public void onSaveStart() {
        ......
    }
});

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, f);
fragmentTransaction.commit();

2.Fragment Arguments

Fragment part code:

public class TestFragment extends Fragment  
{  

    private String mArgument;  
    public static final String ARGUMENT = "argument";  

    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);   
        Bundle bundle = getArguments();  
        if (bundle != null)  
            mArgument = bundle.getString(ARGUMENT);  

    }  

    /** 
     * Pass in the required parameters and set them to arguments 
     * @param argument 
     * @return 
     */  
    public static TestFragment newInstance(String argument)  
    {  
        Bundle bundle = new Bundle();  
        bundle.putString(ARGUMENT, argument);  
        TestFragment f = new TestFragment();  
        f.setArguments(bundle);  
        return f;  
    }

Fragment adds a new Instance static method to be called when instantiating. The parameters needed are passed in to the bundle, then set Arguments (bundles), and finally acquired in onCreate (Intent calls between activities can also be done in a similar way, as detailed in the first line of code Activity section of Guo Lin's god).

Note:
The setArguments method must be completed after the fragment is created and before it is added to Activity. Never call add first and then set arguments.

4. Fragment overlap problem

When the screen rotates or memory restarts (fragments and container activities are re-initialized when they are recycled and re-opened by the system), fragments overlap is caused because the fragments are restored when the activity itself restarts, and then the code that creates the fragments creates a new Fragment.

Solution: In the onCreate method, the parameter Bundle saved Instance State is judged, the Fragment instance is initialized for space-time, and the data is recovered in the Fragment by onSave Instance State method.

Code:

private TestFragment f;

protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);    
        setContentView(R.layout.activity_main);  

        Log.e(TAG, savedInstanceState+"");  

        if(savedInstanceState == null)  
        {  
            f = new TestFragment();  
            FragmentManager fm = getSupportFragmentManager();  
            FragmentTransaction tx = fm.beginTransaction();  
            tx.add(R.id.id_content, f, "ONE");  
            tx.commit();  
        }  



    }  

Fragment and ActionBar and Menu Item

Fragment can add its own MenuItem to ActionBar of Activity or to an optional menu.
a. Call setHasOptionsMenu(true) in Fragment's onCreate.
b. Then implement onCreateOptionsMenu in the Fragment class.
c. If you want to handle MenuItem clicks in Fragment, you can also implement onOptions Item Selected; Activity can also directly handle the MenuItem click events.

Fragment part code:

public class TestFragment extends Fragment  
{  

    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setHasOptionsMenu(true);  
    }  

    ......

    @Override  
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)  
    {  
        inflater.inflate(R.menu.fragment_menu, menu);  
    }  

    @Override  
    public boolean onOptionsItemSelected(MenuItem item)  
    {  
        switch (item.getItemId())  
        {  
        case R.id.id_menu_test:  
            ...... 
            break;  
        }  
        return true;  
    }  

} 

Activity code:

    @Override  
    public boolean onCreateOptionsMenu(Menu menu)  
    {  
        super.onCreateOptionsMenu(menu);  
        getMenuInflater().inflate(R.menu.main, menu);  
        return true;  
    }  

    @Override  
    public boolean onOptionsItemSelected(MenuItem item)  
    {  
        switch (item.getItemId())  
        {  
        case R.id.action_settings:  
            ......  
            return true;  
        default:  
            //If you want Fragment to handle MenuItem click events by itself, don't forget to call super.xxx  
            return super.onOptionsItemSelected(item);  
        }  
    }  

Note: If Fragmenr wants to handle MenuItem click events himself, it must call super.xx

6. Fragment without Layout - Store a lot of data

It is mainly used to deal with the problem of data preservation caused by asynchronous requests, especially the phenomenon of screen rotation when the asynchronous requests are not completed. The steps are as follows:
1. Inherit Fragment and declare that the reference points to your stateful object
2. Call setRetainInstance(boolean) when Fragment is created
3. Add Fragment instances to Activity
4. When Activity restarts, use Fragment Manager to restore Fragment

Fragment part code:

public class TestFragment extends Fragment  
{  

    // data object we want to retain  
    // Save an asynchronous task  
    private MyAsyncTask data;  

    // this method is only called once for this fragment  
    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        // retain this fragment  
        setRetainInstance(true);  
    }  

    public void setData(MyAsyncTask data)  
    {  
        this.data = data;  
    }  

    public MyAsyncTask getData()  
    {  
        return data;  
    }  


}

AsyncTask part code:

public class MyAsyncTask extends AsyncTask<Void, Void, Void>  
{  
    private FixProblemsActivity activity;  
    /** 
     * Is it finished? 
     */  
    private boolean isCompleted;  
    /** 
     * Progress box 
     */  
    private LoadingDialog mLoadingDialog;  
    private List<String> items;  

    public MyAsyncTask(FixProblemsActivity activity)  
    {  
        this.activity = activity;  
    }  

    /** 
     * Start by displaying the load box 
     */  
    @Override  
    protected void onPreExecute()  
    {  
        // Create dialog boxes using Dialog Fragment
        mLoadingDialog = new LoadingDialog();  
        mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
    }  

    /** 
     * Loading data 
     */  
    @Override  
    protected Void doInBackground(Void... params)  
    {  
        items = loadingData();  
        return null;  
    }  

    /** 
     * Load completion calls back the current Activity 
     */  
    @Override  
    protected void onPostExecute(Void unused)  
    {  
        isCompleted = true;  
        notifyActivityTaskCompleted();  
        if (mLoadingDialog != null)  
            mLoadingDialog.dismiss();  
    }  

    public List<String> getItems()  
    {  
        return items;  
    }  

    private List<String> loadingData()  
    {  
        try  
        {  
            Thread.sleep(5000);  
        } catch (InterruptedException e)  
        {  
        }  
        return new ArrayList<String>(Arrays.asList("adopt Fragment Keep a large amount of data",  
                "onSaveInstanceState Save data",  
                "getLastNonConfigurationInstance Has been discarded", "RabbitMQ", "Hadoop",  
                "Spark"));  
    }  

    /** 
     * Set Activity, because Activity changes all the time, set null in onDestroy
     *  
     * @param activity 
     */  
    public void setActivity(FixProblemsActivity activity)  
    {  
        // If the previous Activity is destroyed, the Dialog Fragment bound to the previous Activity is destroyed  
        if (activity == null)  
        {  
            mLoadingDialog.dismiss();  
        }  
        // Set to Current Activity  
        this.activity = activity;  
        // Open a wait box bound to the current Activity  
        if (activity != null && !isCompleted)  
        {  
            mLoadingDialog = new LoadingDialog();  
            mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
        }  
        // If completed, notify Activity  
        if (isCompleted)  
        {  
            notifyActivityTaskCompleted();  
        }  
    }  

    private void notifyActivityTaskCompleted()  
    {  
        if (null != activity)  
        {  
            activity.onTaskCompleted();  
        }  
    }  

}  

Activity part code:

public class FixProblemsActivity extends ListActivity  
{  
    private static final String TAG = "MainActivity";  
    private ListAdapter mAdapter;  
    private List<String> mDatas;  
    private OtherRetainedFragment dataFragment;  
    private MyAsyncTask mMyTask;  

    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        Log.e(TAG, "onCreate");  

        // find the retained fragment on activity restarts  
        FragmentManager fm = getFragmentManager();  
        dataFragment = (OtherRetainedFragment) fm.findFragmentByTag("data");  

        // create the fragment and data the first time  
        if (dataFragment == null)  
        {  
            // add the fragment  
            dataFragment = new OtherRetainedFragment();  
            fm.beginTransaction().add(dataFragment, "data").commit();  
        }  
        mMyTask = dataFragment.getData();  
        if (mMyTask != null)  
        {  
            mMyTask.setActivity(this);  
        } else  
        {  
            mMyTask = new MyAsyncTask(this);  
            dataFragment.setData(mMyTask);  
            mMyTask.execute();  
        }  
        // the data is available in dataFragment.getData()  
    }  


    @Override  
    protected void onRestoreInstanceState(Bundle state)  
    {  
        super.onRestoreInstanceState(state);  
        Log.e(TAG, "onRestoreInstanceState");  
    }  


    @Override  
    protected void onSaveInstanceState(Bundle outState)  
    {  
        mMyTask.setActivity(null);  
        super.onSaveInstanceState(outState);  
        Log.e(TAG, "onSaveInstanceState");  
    }  

    @Override  
    protected void onDestroy()  
    {  
        Log.e(TAG, "onDestroy");  
        super.onDestroy();  

    }  
    /** 
     * Callback 
     */  
    public void onTaskCompleted()  
    {  
        mDatas = mMyTask.getItems();  
        mAdapter = new ArrayAdapter<String>(FixProblemsActivity.this,  
                android.R.layout.simple_list_item_1, mDatas);  
        setListAdapter(mAdapter);  
    }  

}  

VII. The Use of Dialog Fragment

There is a basically consistent declaration cycle with Fragment. And Dialog Fragment also allows developers to reuse Dialog as an embedded component, similar to Fragment (which can show different effects on large and small screens). Using Dialog Fragment requires at least the implementation of onCreateView or onCreateDIalog methods. OnCreateView displays Dialog using a defined xml layout file. OnCreateDialog is the use of Alert Dialog or Dialog to create a Dialog.

1. Rewrite onCreateView to create Dialog
A. Create a dialog layout file
b. Inherit DialogFragment and rewrite the onCreateView method:

public class TestFragment extends DialogFragment  
{  


    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  
            Bundle savedInstanceState)  
    {  
       // Hide the title bar of the dialog box
       getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); 
        View view = inflater.inflate(R.layout.fragment_edit_name, container);  
        return view;  
    }  

} 

c. Call in Activity:

public void showDialog(View view)  
    {  
        TestDialogFragment dialog = new TestDialogFragment();  
        dialog.show(getFragmentManager(), "TestDialog");  
    }  

2. Rewrite onCreateDialog to create Dialog
a. New Dialog Layout File
b. Inherit the DialogFragment override onCreateDialog method:

public class TestFragment extends DialogFragment  
{  

    @Override  
    public Dialog onCreateDialog(Bundle savedInstanceState)  
    {  
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());  
        // Get the layout inflater  
        LayoutInflater inflater = getActivity().getLayoutInflater();  
        View view = inflater.inflate(R.layout.fragment_test_dialog, null);  
        // Inflate and set the layout for the dialog  
        // Pass null as the parent view because its going in the dialog layout  
        builder.setView(view)  
                // Add action buttons  
                .setPositiveButton("Test",  
                        new DialogInterface.OnClickListener()  
                        {  
                            @Override  
                            public void onClick(DialogInterface dialog, int id)  
                            {  
                            }  
                        }).setNegativeButton("Cancel", null);  
        return builder.create();  
    }  
} 

c. calls:

public void showDialog(View view)  
    {  
        TestFragment dialog = new TestFragment();  
        dialog.show(getFragmentManager(), "testDialog");  
    } 

8. Fragment's Start Activity ForResult

There are startActivityForResult () and onActivityResult () methods in Fragment, which need to be set to return by calling getActivity().setResult(), Fragment.REQUEST_CODE, intent).

Part of the code:

// incoming data
Intent intent = new Intent(getActivity(),ContentActivity.class);  
intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));  
startActivityForResult(intent, REQUEST_DETAIL);  

    @Override  
    public void onActivityResult(int requestCode, int resultCode, Intent data)  
    {  
        Log.e("TAG", "onActivityResult");  
        super.onActivityResult(requestCode, resultCode, data);  
        if(requestCode == REQUEST_DETAIL)  
        {  
            mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- "+data.getStringExtra(ContentFragment.RESPONSE));  
            mAdapter.notifyDataSetChanged();  
        }  
    } 




    // Return data
    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        Bundle bundle = getArguments();  
        if (bundle != null)  
        {  
            mArgument = bundle.getString(ARGUMENT);  
            Intent intent = new Intent();  
            intent.putExtra(RESPONSE, "good");  
            getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);  
        }  

    }  

    public static ContentFragment newInstance(String argument)  
    {  
        Bundle bundle = new Bundle();  
        bundle.putString(ARGUMENT, argument);  
        ContentFragment contentFragment = new ContentFragment();  
        contentFragment.setArguments(bundle);  
        return contentFragment;  
    }  

9. Differences between FragmentPager Adapter and FragmentStatePager Adapter

The main difference between using ViewPager and combining any of the above instances to make an APP home page is whether fragment s are destroyed or not:
FragmentPagerAdapter: For fragments that are no longer needed, choose to call the detach method to destroy only the view and not the fragment instance.
FragmentStatePager Adapter: It destroys fragments that are no longer needed. When the current transaction is committed, it completely removes fragmeng from the FragmentManager of the current Activity. When the state is marked, the bundle information in its onSave Instance State (Bundle Out State) is saved. When the user switches back, it can be restored to generate new fragments through the bundle. That is, you can save some data in the onSave Instance State (Bundle out State) method and restore it in onCreate.
As mentioned above, using FragmentStatePager Adapter will certainly save more memory, but it will take time to destroy new ones. Generally speaking, if you are making the main page, there are 3 or 4 Tab s, then you can choose to use FragmentPager Adapter. If you are using ViewPager to display a very large number of items, it is recommended to use FragmentStatePager Adapter.

X. Data Transfer between Fragment s

Call Fragment. setTarget Fragment, which is generally used when the current fragment is started by other fragments.

Part of the code:

EvaluateDialog dialog = new EvaluateDialog();  
//Note the setTarget Fragment  
dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);  
dialog.show(getFragmentManager(), EVALUATE_DIALOG); 

//Receive the returned data  
@Override  
public void onActivityResult(int requestCode, int resultCode, Intent data)  
{  
    super.onActivityResult(requestCode, resultCode, data);  

    if (requestCode == REQUEST_EVALUATE)  
    {  
        String evaluate = data  
                    .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);  
        Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();  
        Intent intent = new Intent();  
        intent.putExtra(RESPONSE, evaluate);  
        getActivity().setResult(Activity.REQUEST_OK, intent);  
        }  

    } 




public class EvaluateDialog extends DialogFragment  
{ 
    ......

    // Setting Return Data  
    protected void setResult(int which)  
    {  
        // Determine if targetFragment is set  
        if (getTargetFragment() == null)  
            return;  

        Intent intent = new Intent();  
        intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);  
        getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,  Activity.RESULT_OK, intent);            
    }  
}

summary

This chapter introduces the various applications of Fragmentation. The next chapter mainly studies and analyses YoKey's blog and Fragmentation, an open source framework developed by God.

Posted by ludjer on Wed, 29 May 2019 11:27:53 -0700