Activity与Activity之间,Fragment与Fragment之间通过Bundle传值的研究

一、Fragment与Activity的通讯

 

在使用fragment的时候,通常的用法都是使用一个activity来管理不同的fragment,所以每个fragment与activity的及时通讯就很重要。

1、Fragment可以调用getActivity()方法很容易的得到它所在的activity的对象,然后就可以查找activity中的控件们(findViewById())。例如:

Viewlist View =getActivity().findViewById(R.id.list);
同样的,activity也可以通过FragmentManager的方法查找它所包含的frament们。例如:
ExampleFragment fragment =(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment) 

但是这个并不是最好的办法,根据fragmen的定义,我们可以在fragment里面定义一个回调接口,在activity中实现它。举个谷歌官方给的例子,这是一个新闻程序的例子,它有一个activity,activity中含有两个fragment。fragmentA显示新闻标题,fragmentB显示标题对应的内容。fragmentA必须在用户选择了某个标题时告诉activity,然后activity再告诉fragmentB,fragmentB就显示出对应的内容(为什么这么麻烦?直接fragmentA告诉fragmentB不就行了?也可以啊,但是你的fragment就减少了可重用的能力。现在我只需把我的事件告诉宿主,由宿主决定如何处置,这样是不是重用性更好呢?)。如下例:

复制代码
public class PersionListFragment extends ListFragment {  
    private static final String STATE_ACTIVATED_POSITION = "activated_position";  
    private Callbacks mCallbacks = sDummyCallbacks;  
    private int mActivatedPosition = ListView.INVALID_POSITION;  
    public interface Callbacks {  
        public void onItemSelected(String id);  
    }  
    private static Callbacks sDummyCallbacks = new Callbacks() {  
        @Override  
        public void onItemSelected(String id) {  
        }  
    }; 
复制代码
我们在PersionListFragment中定义了mCallbacks这个接口,当然了我们自己实现了一个空的接口内容sDummyCallbacks,当作mCallbacks的默认接口。
下面就是我们要到Activity中去实现这个接口:
复制代码
public class PersionListActivity extends FragmentActivity implements PersionListFragment.Callbacks {  
    private boolean mTwoPane;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_persion_twopane);  
        if (findViewById(R.id.persion_detail_container) != null) {  
            mTwoPane = true;  
            ((PersionListFragment) getSupportFragmentManager().findFragmentById(R.id.persion_list)).setActivateOnItemClick(false);  
        }  
    }  
  
    @Override  
    public void onItemSelected(String id) {  
        if (mTwoPane) {  
            Bundle arguments = new Bundle();  
            arguments.putString(PersionDetailFragment.ARG_ITEM_ID, id);  
            PersionDetailFragment fragment = new PersionDetailFragment();  
            fragment.setArguments(arguments);  
            getSupportFragmentManager().beginTransaction().replace(R.id.persion_detail_container, fragment).commit();  
        } else {  
            Intent detailIntent = new Intent(this, PersionDetailActivity.class);  
            detailIntent.putExtra(PersionDetailFragment.ARG_ITEM_ID, id);  
            startActivity(detailIntent);  
        }  
    }  
}
复制代码
 
那么实现完了接口怎么传给刚才的fragment呢?
当fragment添加到activity中时,会调用fragment的方法onAttach(),这个方法中适合检查activity是否实现了OnArticleSelectedListener接口,检查方法就是对传入的activity的实例进行类型转换,然后赋值给我们在fragment中定义的接口。
复制代码
@Override  
   public void onAttach(Activity activity) {  
       super.onAttach(activity);  
       if (!(activity instanceof Callbacks)) {  
           throw new IllegalStateException("Activity must implement fragment's callbacks.");  
       }  
       mCallbacks = (Callbacks) activity;  
   }  
  
   @Override  
   public void onDetach() {  
       super.onDetach();  
       mCallbacks = sDummyCallbacks;  
   }  
  
   @Override  
   public void onListItemClick(ListView listView, View view, int position, long id) {  
       super.onListItemClick(listView, view, position, id);  
       mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);  
   } 
复制代码
 
在一个fragment从activity中剥离的时候,就会调用onDetach方法,这个时候正好可以把它的接口恢复为默认接口。防止不必要的错误。注意看onAttach方法中的代码,在赋值之前要做一个判断,看看Activity中有没有实现了这个接口,用到了instanceof。如果没有实现接口,我们就抛出异常。
这个时候我们就实现了Fragment与Activity之间的通讯,把主要的事情还是都交给管理员Activity做比较好。
http://download.csdn.net/detail/my1314my/5217862
传值场景

通常来说在页面之间传递数据有两种情况,Activity传递Activity、Fragment传递给Fragment。这两种情况都使用到了Bundle,只是使用的方式有一些差别。

传值方法
Activity To Activity:

Activity之间的调用需要通过Intent来实现,那么要传递的数据也需要封装到Bundle中,通过Intent传递到接收Activity中。例如如下代码:

复制代码
SaveProject nTestObject = new SaveProject();  
Bundle nBundle = new Bundle();  
nBundle.putParcelable("PROJECT", nTestObject);  
nBundle.putString("NAME", nTestObject.toString());  
Intent nIteIntent = new Intent(FirstActivity.this, SecondActivity.class);  
nIteIntent.putExtras(nBundle);  
FirstActivity.this.startActivity(nIteIntent);  
复制代码
Fragment To Fragment:

Fragment之间的调用就如同普通的类一样,直接调用构造方法来实例化Fragment对象即可。建议的传值方式是通过Bundle来传递,而不是直接作为构造参数传递。首先,官方建议在每个Fragment类中实现如下的构造方法:

public static SecondFragment getInstance(Bundle bundle) {  
    SecondFragment secondFragment = new SecondFragment();  
    secondFragment.setArguments(bundle);  
    return secondFragment;  
}

那么在需要调用某个Fragment时,执行类似如下代码:

复制代码
SaveProject nTestObject = new SaveProject();  
Bundle nBundle = new Bundle();  
nBundle.putParcelable("PROJECT", nTestObject);  
nBundle.putString("NAME", nTestObject.toString());  
SecondFragment secondFragment = SecondFragment.getInstance(nBundle);  
android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction();  
transaction.replace(R.id.fragment_id, secondFragment);  
transaction.commit();  
复制代码

接收参数:

//在onActivityCreated(Bundle bundle)方法中
if(getArguments()!=null)
{
    tabid = getArguments().getInt("tabid");
}

 

差异现象

为了测试两种传输方式传递对象的方式是否相同,我分别通过Bundle传递了一个对象new SaveProject(),在接收到对象之后打印出传递前后对象的内存信息。参考以下打印内容不难发现,通过Intent传递的Bundle中包含的对象属于拷贝,即Activity_A中的对象通过Bundle传递给Activity_B,后者接收的对象与之前发送的对象不是同一个。而Fragment之间通过构造参数传递的Bundle中的对象传递的应该属于引用。

Activity:

12-10 16:32:47.953: D/bundle_test(14691): Activity_send = com.panda.bundledatatest.SaveProject@40fb3668
12-10 16:32:47.953: D/bundle_test(14691): ---------------------------
12-10 16:32:47.953: D/bundle_test(14691): Activity_get = com.panda.bundledatatest.SaveProject@40fbf180

 

12-10 16:33:20.935: D/bundle_test(14691): Fragment_send = com.panda.bundledatatest.SaveProject@40fd5e20
12-10 16:33:20.935: D/bundle_test(14691): ---------------------------
12-10 16:33:20.935: D/bundle_test(14691): Fragment_get = com.panda.bundledatatest.SaveProject@40fd5e20

差异分析

1、intent传递对象时,传递的是一个副本(深拷贝)。所以在android中,intent进行数据传递时,基本数据类型和对象传递的都是副本,改变传递过来的值,不会改变原来的值。

2、如果单纯的构造参数传值,Bundle的对象是相同的内存地址,也就是说接受者拿到的只是发送者的一个引用而已。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值