ale*_*pfx 45 android master-detail android-fragments android-activity android-architecture-components
在Last Google IO上,Google发布了一些新的Arch组件预览,其中一个是ViewModel.
在文档中, Google展示了此组件的一种可能用途:
活动中的两个或更多个片段需要彼此通信是很常见的.这绝不是微不足道的,因为两个片段都需要定义一些接口描述,并且所有者活动必须将两者绑定在一起.而且,两个片段必须处理尚未创建或不可见的其他片段的情况.
可以使用ViewModel对象解决这个常见的痛点.想象一下master-detail片段的常见情况,其中我们有一个片段,用户从列表中选择一个项目,另一个片段显示所选项目的内容.
这些片段可以使用其活动范围共享ViewModel来处理此通信.
并显示了一个实现示例:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
Run Code Online (Sandbox Code Playgroud)
我很高兴不需要那些用于片段的接口通过活动进行通信.
但Google的示例并没有准确显示我将如何从master调用详细信息片段.
我仍然必须使用一个将由activity实现的接口,它将调用fragmentManager.replace(...),或者还有另一种方法可以使用新的架构来实现它?
Lon*_*ger 42
2017年6月6日更新,
Android官方提供了一个简单,精确的示例来说明ViewModel如何在Master-Detail模板上工作,您应该首先看一下它.在片段之间共享数据
正如@CommonWare,@ Quang Nguyen所做的那样,Yigit的目的不是从主人到细节进行呼叫,而是更好地使用中间人模式.但是如果你想做一些片段事务,它应该在活动中完成.此时,ViewModel类应该作为Activity中的静态类,并且可能包含一些Ugly Callback来回调活动以进行片段事务.
我试图实现这个并做一个关于这个的简单项目.你可以看一下.大多数代码都是从Google IO 2017引用的,也就是结构. https://github.com/charlesng/SampleAppArch
我不使用Master Detail Fragment来实现组件,而是使用旧的组件(ViewPager中的片段之间的通信.)逻辑应该是相同的.
但我发现使用这些组件有些重要
1.Pager活动
public class PagerActivity extends LifecycleActivity {
/**
* 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 = ViewModelProviders.of(this).get(PagerAgentViewModel.class);
pagerAgentViewModel.init();
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
...Pager Implementation
}
}
Run Code Online (Sandbox Code Playgroud)
2.PagerAgentViewModel(它应该得到一个更好的名字,而不是这个)
public class PagerAgentViewModel extends ViewModel {
private MutableLiveData<String> messageContainerA;
private MutableLiveData<String> messageContainerB;
public void init()
{
messageContainerA = new MutableLiveData<>();
messageContainerA.setValue("Default Message");
messageContainerB = new MutableLiveData<>();
messageContainerB.setValue("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;
}
}
Run Code Online (Sandbox Code Playgroud)
3.BlankFragmentA
public class BlankFragmentA extends LifecycleFragment {
public BlankFragmentA() {
// Required empty public constructor
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//setup the listener for the fragment A
ViewModelProviders.of(getActivity()).get(PagerAgentViewModel.class).getMessageContainerA().observe(this, 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);
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) {
ViewModelProviders.of(getActivity()).get(PagerAgentViewModel.class).sendMessageToB("Hello B");
}
});
return view;
}
}
Run Code Online (Sandbox Code Playgroud)
4.BlankFragmentB
public class BlankFragmentB extends LifecycleFragment {
public BlankFragmentB() {
// Required empty public constructor
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//setup the listener for the fragment B
ViewModelProviders.of(getActivity()).get(PagerAgentViewModel.class).getMessageContainerB().observe(this, 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);
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) {
ViewModelProviders.of(getActivity()).get(PagerAgentViewModel.class).sendMessageToA("Hello A");
}
});
return view;
}
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*iel 19
正如谷歌官方教程中所写,现在你可以获得一个共享视图模型by activityViewModels()
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
Run Code Online (Sandbox Code Playgroud)
Ami*_*dat 11
根据google codelabs 示例,我找到了与其他人类似的解决方案.我有两个片段,其中一个在另一个片段中等待对象更改,并继续使用更新的对象进行处理.
对于这种方法,您将需要一个ViewModel类,如下所示:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
并且侦听器片段应如下所示:
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.
}
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
最后,updater片段可以是这样的:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
值得一提的是updater片段可以是任何形式的片段(不仅仅是DialogFragment),对于使用这些体系结构组件,您应该在app build.gradle文件中包含这些代码行.资源
dependencies {
def lifecycle_version = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}
Run Code Online (Sandbox Code Playgroud)
我实现了类似于你想要的东西,我的viewmodel包含包含Enum状态的LiveData对象,当你想要将片段从master更改为细节(或反过来)时,你调用ViewModel函数来改变livingata值,并且活动知道更改片段,因为它正在观察livingata对象.
TestViewModel:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
枚举:
public class Enums {
public enum state {
Master,
Detail
}
}
Run Code Online (Sandbox Code Playgroud)
测试活动:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
MasterFragment:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
DetailFragment:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
在使用附加到Activity的回调之前,该回调被视为容器。
该回调是两个Fragment之间的中间人。此先前解决方案的坏处是:
使用新的ViewModel(支持LiveData),您将获得一个优雅的解决方案。它现在扮演中间人的角色,您可以将其生命周期附加到Activity。
现在,您完全摆脱了与活动和相关片段紧密耦合的回调。
我强烈建议您通过Google的代码实验室。在步骤5中,您可以找到一个很好的示例。
归档时间: |
|
查看次数: |
46650 次 |
最近记录: |