سؤال

I'm using a template to build a tabbed app which employs a custom class (that extends Fragment) and
how can I pass values from the first fragment to the second one?

Description: In a package three classes control the creation of tabs and views:

Tabs: package com.research.fragmenttabstudy.base;

public class AppConstants {
       public static final String TAB_A  = "tab_a_identifier";
       public static final String TAB_B  = "tab_b_identifier";

       //Your other constants, if you have them..

}

Fragments:

package com.research.fragmenttabstudy.base;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;

public class BaseFragment extends Fragment {
    public AppMainTabActivity mActivity;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mActivity       =   (AppMainTabActivity) this.getActivity();
    }

    public boolean onBackPressed(){
        return false;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data){

    }
}

And the mainActivity:

package com.research.fragmenttabstudy.base;

import java.util.HashMap;
import java.util.Stack;

import com.research.fragmenttabstudy.R;
import com.research.fragmenttabstudy.tabA.AppTabAFirstFragment;
import com.research.fragmenttabstudy.tabB.AppTabBFirstFragment;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TabHost;

public class AppMainTabActivity extends FragmentActivity {
    /* Your Tab host */
    private TabHost mTabHost;

    /* A HashMap of stacks, where we use tab identifier as keys..*/
    private HashMap<String, Stack<Fragment>> mStacks;

    /*Save current tabs identifier in this..*/
    private String mCurrentTab;

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

        /*  
         *  Navigation stacks for each tab gets created.. 
         *  tab identifier is used as key to get respective stack for each tab
         */
        mStacks             =   new HashMap<String, Stack<Fragment>>();
        mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
        mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());

        mTabHost                =   (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setOnTabChangedListener(listener);
        mTabHost.setup();

        initializeTabs();



//Receive intent from Fragment AppTabBFirstFragment
        Intent intent = getIntent();
        String url = intent.getStringExtra("URLone");

        //Send data to AppTabBSecondFragment
        Bundle bundle=new Bundle();
        bundle.putString("urlToSecond", url);
        AppTabBSecondFragment objFragment = new AppTabBSecondFragment();
        objFragment.setArguments(bundle);        }


    private View createTabView(final int id) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView =   (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        return view;
    }

    public void initializeTabs(){
        /* Setup your tab icons and content views.. Nothing special in this..*/
        TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);
        mTabHost.setCurrentTab(-3);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_a_state_btn));
        mTabHost.addTab(spec);


        spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_b_state_btn));
        mTabHost.addTab(spec);
    }


    /*Comes here when user switch tab, or we do programmatically*/
    TabHost.OnTabChangeListener listener    =   new TabHost.OnTabChangeListener() {
      public void onTabChanged(String tabId) {
        /*Set current tab..*/
        mCurrentTab                     =   tabId;

        if(mStacks.get(tabId).size() == 0){
          /*
           *    First time this tab is selected. So add first fragment of that tab.
           *    Dont need animation, so that argument is false.
           *    We are adding a new fragment which is not present in stack. So add to stack is true.
           */
          if(tabId.equals(AppConstants.TAB_A)){
            pushFragments(tabId, new AppTabAFirstFragment(), false,true);
          }else if(tabId.equals(AppConstants.TAB_B)){
            pushFragments(tabId, new AppTabBFirstFragment(), false,true);
          }
        }else {
          /*
           *    We are switching tabs, and target tab is already has atleast one fragment. 
           *    No need of animation, no need of stack pushing. Just show the target fragment
           */
          pushFragments(tabId, mStacks.get(tabId).lastElement(), false,false);
        }
      }
    };


    /* Might be useful if we want to switch tab programmatically, from inside any of the fragment.*/
    public void setCurrentTab(int val){
          mTabHost.setCurrentTab(val);
    }


    /* 
     *      To add fragment to a tab. 
     *  tag             ->  Tab identifier
     *  fragment        ->  Fragment to show, in tab identified by tag
     *  shouldAnimate   ->  should animate transaction. false when we switch tabs, or adding first fragment to a tab
     *                      true when when we are pushing more fragment into navigation stack. 
     *  shouldAdd       ->  Should add to fragment navigation stack (mStacks.get(tag)). false when we are switching tabs (except for the first time)
     *                      true in all other cases.
     */
    public void pushFragments(String tag, Fragment fragment,boolean shouldAnimate, boolean shouldAdd){
      if(shouldAdd)
          mStacks.get(tag).push(fragment);
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      if(shouldAnimate)
          ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }


    public void popFragments(){
      /*    
       *    Select the second last fragment in current tab's stack.. 
       *    which will be shown after the fragment transaction given below 
       */
      Fragment fragment             =   mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);

      /*pop current fragment from stack.. */
      mStacks.get(mCurrentTab).pop();

      /* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }   


    @Override
    public void onBackPressed() {
        if(((BaseFragment)mStacks.get(mCurrentTab).lastElement()).onBackPressed() == false){
            /*
             * top fragment in current tab doesn't handles back press, we can do our thing, which is
             * 
             * if current tab has only one fragment in stack, ie first fragment is showing for this tab.
             *        finish the activity
             * else
             *        pop to previous fragment in stack for the same tab
             * 
             */
            if(mStacks.get(mCurrentTab).size() == 1){
                super.onBackPressed();  // or call finish..
            }else{
                popFragments();
            }
        }else{
            //do nothing.. fragment already handled back button press.
        }
    }


    /*
     *   Imagine if you wanted to get an image selected using ImagePicker intent to the fragment. Ofcourse I could have created a public function
     *  in that fragment, and called it from the activity. But couldn't resist myself.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(mStacks.get(mCurrentTab).size() == 0){
            return;
        }

        /*Now current fragment on screen gets onActivityResult callback..*/
        mStacks.get(mCurrentTab).lastElement().onActivityResult(requestCode, resultCode, data);
    }
}

Now, a new package contains the classes associated to a tab, keeping this tab always visible:

package com.research.fragmenttabstudy.tabB;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

import com.research.fragmenttabstudy.R;
import com.research.fragmenttabstudy.base.AppConstants;
import com.research.fragmenttabstudy.base.BaseFragment;

public class AppTabBFirstFragment extends BaseFragment {
        private Button itemOne;
        String itemOneUrl = "http://url”;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View view       =   inflater.inflate(R.layout.app_tab_b_first_screen, container, false);


        itemOne =   (Button) view.findViewById(R.id.itemOne);
        itemOne.setOnClickListener(listener);
                return view;
    }

    private OnClickListener listener        =   new View.OnClickListener(){
        @Override
        public void onClick(View v){
            // Go to next fragment in navigation stack


//Callback
    Intent intent = new Intent (getActivity().getBaseContext(), AppMainTabActivity.class);
    intent.putExtra("URLone", itemOneUrl);
    getActivity().startActivity(intent);

            mActivity.pushFragments(AppConstants.TAB_B, new AppTabBSecondFragment(),true,true);
        }
    };
 }

And here is the class for the second fragment:

package com.research.fragmenttabstudy.tabB;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.research.fragmenttabstudy.R;
import com.research.fragmenttabstudy.base.BaseFragment;

public class AppTabBSecondFragment extends BaseFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view       =   inflater.inflate(R.layout.app_tab_b_second_screen, container, false);


        TextView tab = (TextView) view.findViewById(R.id.tab);
    String urlOne = getArguments().getString("urlToSecond");
    tab.setText(urlOne);


        return view;
    }
}
هل كانت مفيدة؟

المحلول

You use callback interfaces to pass data from the Fragment to its Activity. The Activity then finds the Fragment to receive the data and (if the Fragment exists) calls a method in that Fragment to pass the data (or creates the Fragment if it doesn't exist yet).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top