Android 使用新的体系结构组件ViewModel在片段之间共享数据
在上一次谷歌IO上,谷歌发布了一些新arch组件的预览,其中之一是ViewModel 在中,google展示了此组件的一种可能用途: 一个活动中的两个或多个片段需要修改是很常见的 相互交流。这从来都不是小事,因为这两个片段 需要定义一些接口描述,并且所有者活动必须 把两者绑在一起。此外,两个片段都必须处理这个案例 另一个片段尚未创建或不可见 使用ViewModel对象可以解决这个常见的难点。 想象一个主细节片段的常见情况,我们有一个 用户从一个列表和另一个列表中选择一个项目的片段 显示所选项目内容的片段 这些片段可以使用其活动范围共享一个ViewModel 处理这个通讯 并显示了一个实现示例:Android 使用新的体系结构组件ViewModel在片段之间共享数据,android,android-fragments,android-activity,master-detail,android-architecture-components,Android,Android Fragments,Android Activity,Master Detail,Android Architecture Components,在上一次谷歌IO上,谷歌发布了一些新arch组件的预览,其中之一是ViewModel 在中,google展示了此组件的一种可能用途: 一个活动中的两个或多个片段需要修改是很常见的 相互交流。这从来都不是小事,因为这两个片段 需要定义一些接口描述,并且所有者活动必须 把两者绑在一起。此外,两个片段都必须处理这个案例 另一个片段尚未创建或不可见 使用ViewModel对象可以解决这个常见的难点。 想象一个主细节片段的常见情况,我们有一个 用户从一个列表和另一个列表中选择一个项目的片段 显示所选项目内
public class SharedViewModel extends ViewModel {
private final SavedStateHandle state;
public SharedViewModel(SavedStateHandle state) {
this.state = state;
}
private final MutableLiveData<Item> selected = state.getLiveData("selected");
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
@Override
protected void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
@Override
protected void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
公共类SharedViewModel扩展了ViewModel{
私有最终SavedStateHandle状态;
public SharedViewModel(SavedStateHandle状态){
this.state=状态;
}
private final MutableLiveData selected=state.getLiveData(“selected”);
公共作废选择(项目){
已选择。设置值(项目);
}
公共LiveData getSelected(){
返回选中的;
}
}
公共类主片段扩展片段{
私有共享模型;
@凌驾
已创建受保护的空视图(视图、捆绑包savedInstanceState){
super.onViewCreated(视图,savedInstanceState);
model=newviewmodelprovider(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(项->{
型号。选择(项目);
});
}
}
公共类DetailFragment扩展了片段{
@凌驾
已创建受保护的空视图(视图、捆绑包savedInstanceState){
super.onViewCreated(视图,savedInstanceState);
SharedViewModel model=newviewmodelprovider(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(此,{item->
//更新用户界面
});
}
}
对于不需要那些用于片段的接口就可以通过活动进行通信,我感到非常兴奋
但谷歌的例子并没有确切地显示我如何从master调用细节片段
我仍然必须使用将由活动实现的方法,该方法将调用fragmentManager.replace(…),或者有另一种方法使用新的体系结构来实现这一点?在使用附加到被视为容器的活动的回调之前。
那个回调是两个片段之间的中间人。 以前的解决方案的缺点是:
- 活动必须进行回调,这意味着需要进行大量的工作 活动
- 两个片段紧密耦合,以后很难更新或更改逻辑
- 两个片段之间的逻辑和数据现在显示在ViewModel中
- 两个片段从ViewModel获取数据/状态,因此它们不需要相互了解
- 此外,借助LiveData的强大功能,您可以根据主片段的更改以反应式方式更改细节片段,而不是以前的回调方式
我强烈推荐你通过。在步骤5中,您可以找到一个很好的例子。您可以像这样设置从细节片段到主片段的值
model.selected.setValue(item)
于2017年6月12日更新, Android Official提供了一个简单、精确的示例,说明ViewModel如何在主详细信息模板上工作,您应该先看看它。 作为@CommonWare,@Quang-Nguyen method,Yigit的目的不是从大师到细节,而是更好地使用中间人模式。但是,如果您想进行一些片段事务,则应该在活动中完成。此时,ViewModel类应该是活动中的静态类,并且可能包含一些丑陋的回调来回调活动以生成片段事务 我已经试着实现了这一点,并为此做了一个简单的项目。你可以看看。大部分代码引用自Google IO 2017,也是结构。 我没有使用Master-Detail片段来实现该组件,但是使用旧的(ViewPager中片段之间的通信)逻辑应该是相同的 但我发现使用这些组件很重要 你想在中间人发送和接收什么,他们应该只在视图模型中发送和接收。
public class PagerActivity extends AppCompatActivity {
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
private PagerAgentViewModel pagerAgentViewModel;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
pagerAgentViewModel = new ViewModelProvider(this).get(PagerAgentViewModel.class);
pagerAgentViewModel.init();
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
...Pager Implementation
}
}
2.PagerAgentViewModel(它应该有一个更好的名字,而不是这个)
公共类PagerAgentViewModel扩展了ViewModel{
私有最终SavedStateHandle状态;
私有最终可变LiveData messageContainerA;
私有最终可变LiveData messageContainerB;
公共页面RagentViewModel(SavedStateHandle状态){
this.state=状态;
messageContainerA=state.getLiveData(“默认消息”);
messageContainerB=state.getLiveData(“默认消息”);
}
public void sendMessageToB(字符串消息)
{
messageContainerB.setValue(msg);
}
公共无效sendMessageToA
public class PagerAgentViewModel extends ViewModel {
private final SavedStateHandle state;
private final MutableLiveData<String> messageContainerA;
private final MutableLiveData<String> messageContainerB;
public PagerAgentViewModel(SavedStateHandle state) {
this.state = state;
messageContainerA = state.getLiveData("Default Message");
messageContainerB = state.getLiveData("Default Message");
}
public void sendMessageToB(String msg)
{
messageContainerB.setValue(msg);
}
public void sendMessageToA(String msg)
{
messageContainerA.setValue(msg);
}
public LiveData<String> getMessageContainerA() {
return messageContainerA;
}
public LiveData<String> getMessageContainerB() {
return messageContainerB;
}
}
public class BlankFragmentA extends Fragment {
private PagerAgentViewModel viewModel;
public BlankFragmentA() {
// Required empty public constructor
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel = new ViewModelProvider(getActivity()).get(PagerAgentViewModel.class);
textView = (TextView) view.findViewById(R.id.fragment_textA);
// set the onclick listener
Button button = (Button) view.findViewById(R.id.btnA);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.sendMessageToB("Hello B");
}
});
//setup the listener for the fragment A
viewModel.getMessageContainerA().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(@Nullable String msg) {
textView.setText(msg);
}
});
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_blank_a, container, false);
return view;
}
}
public class BlankFragmentB extends Fragment {
public BlankFragmentB() {
// Required empty public constructor
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel = new ViewModelProvider(getActivity()).get(PagerAgentViewModel.class);
textView = (TextView) view.findViewById(R.id.fragment_textB);
//set the on click listener
Button button = (Button) view.findViewById(R.id.btnB);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.sendMessageToA("Hello A");
}
});
//setup the listener for the fragment B
viewModel.getMessageContainerB().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(@Nullable String msg) {
textView.setText(msg);
}
});
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_blank_b, container, false);
return view;
}
}
public class SharedViewModel<T> extends ViewModel {
private final MutableLiveData<T> selected = new MutableLiveData<>();
private OnSelectListener<T> listener = item -> {};
public interface OnSelectListener <T> {
void selected (T item);
}
public void setListener(OnSelectListener<T> listener) {
this.listener = listener;
}
public void select(T item) {
selected.setValue(item);
listener.selected(item);
}
public LiveData<T> getSelected() {
return selected;
}
}
SharedViewModel stepViewModel = ViewModelProviders.of(this).get("step", SharedViewModel.class);
stepViewModel.setListener(this);
@Override
public void selected(Step item) {
Log.d(TAG, "selected: "+item);
}
stepViewModel = ViewModelProviders.of(getActivity()).get("step", SharedViewModel.class);
stepViewModel.select(step);
public class TestViewModel extends ViewModel {
private MutableLiveData<Enums.state> mState;
public TestViewModel() {
mState=new MutableLiveData<>();
mState.setValue(Enums.state.Master);
}
public void onDetail() {
mState.setValue(Enums.state.Detail);
}
public void onMaster() {
mState.setValue(Enums.state.Master);
}
public LiveData<Enums.state> getState() {
return mState;
}
}
public class Enums {
public enum state {
Master,
Detail
}
}
public class TestActivity extends LifecycleActivity {
private ActivityTestBinding mBinding;
private TestViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding=DataBindingUtil.setContentView(this, R.layout.activity_test);
mViewModel=ViewModelProviders.of(this).get(TestViewModel.class);
mViewModel.getState().observe(this, new Observer<Enums.state>() {
@Override
public void onChanged(@Nullable Enums.state state) {
switch(state) {
case Master:
setMasterFragment();
break;
case Detail:
setDetailFragment();
break;
}
}
});
}
private void setMasterFragment() {
MasterFragment masterFragment=MasterFragment.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout, masterFragment,"MasterTag").commit();
}
private void setDetailFragment() {
DetailFragment detailFragment=DetailFragment.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout, detailFragment,"DetailTag").commit();
}
@Override
public void onBackPressed() {
switch(mViewModel.getState().getValue()) {
case Master:
super.onBackPressed();
break;
case Detail:
mViewModel.onMaster();
break;
}
}
}
public class MasterFragment extends Fragment {
private FragmentMasterBinding mBinding;
public static MasterFragment newInstance() {
MasterFragment fragment=new MasterFragment();
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding=DataBindingUtil.inflate(inflater,R.layout.fragment_master, container, false);
mBinding.btnDetail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final TestViewModel viewModel=ViewModelProviders.of(getActivity()).get(TestViewModel.class);
viewModel.onDetail();
}
});
return mBinding.getRoot();
}
}
public class DetailFragment extends Fragment {
private FragmentDetailBinding mBinding;
public static DetailFragment newInstance() {
DetailFragment fragment=new DetailFragment();
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding=DataBindingUtil.inflate(inflater,R.layout.fragment_detail, container, false);
mBinding.btnMaster.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final TestViewModel viewModel=ViewModelProviders.of(getActivity()).get(TestViewModel.class);
viewModel.onMaster();
}
});
return mBinding.getRoot();
}
}
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import yourPackage.YourObjectModel;
public class SharedViewModel extends ViewModel {
public MutableLiveData<YourObjectModel> item = new MutableLiveData<>();
public YourObjectModel getItem() {
return item.getValue();
}
public void setItem(YourObjectModel item) {
this.item.setValue(item);
}
}
public class ListenerFragment extends Fragment{
private SharedViewModel model;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.item.observe(getActivity(), new Observer<YourObjectModel>(){
@Override
public void onChanged(@Nullable YourObjectModel updatedObject) {
Log.i(TAG, "onChanged: recieved freshObject");
if (updatedObject != null) {
// Do what you want with your updated object here.
}
}
});
}
}
public class UpdaterFragment extends DialogFragment{
private SharedViewModel model;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
}
// Call this method where it is necessary
private void updateViewModel(YourObjectModel yourItem){
model.setItem(yourItem);
}
}
dependencies {
def lifecycle_version = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
class MainFragment : Fragment() {
private lateinit var viewModel: ViewModel
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// kotlin does not have a getActivity() built in method instead we use activity, which is null-safe
activity?.let {
viemModel = ViewModelProvider(it).get(SharedViewModel::class.java)
}
}
}