我有以下设置:
我有一个Activity
启动了FragmentA
。
FragmentA
包含一个recyclerView
和一个adapter
。
我在interfaceA
中实现的适配器中有一个FragmentA
,以便通知我单击了哪个位置。
interfaceB
中创建了第二个FragmentA
,该Activity
在步骤1中启动FragmentA
的{{1}}中实现。FragmentB
获得的数据从Activity
启动interfaceB
。 一切正常,但是流程繁琐,需要大量样板代码。
目标是要启动activity
的{{1}},其中包含来自fragmentB
内recyclerView中单击的项目的数据。
问题:能否以其他方式实现?
以下代码:
FragmentA
启动FragmentA:
Activity
在Fragment fragment = fragmentManager.findFragmentByTag(FragmentA.class.getName());
if (fragment == null) {
fragment = Fragment.instantiate(this, FragmentA.class.getName());
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.replace(R.id.fragmentLayout, fragment, FragmentA.class.getName())
.addToBackStack(FragmentA.class.getName())
.commit();
内部,我们有FragmentA
,并且在recyclerView
中实现了interfaceA
:
适配器类:
adapter
public class AdapterA extends RecyclerView.Adapter< AdapterA.ViewHolderA> {
//instances
private Context context;
private List<Data> dataList;
private OnItemClickListener onItemListClickListener;
//Constructor
public AdapterA (Context context, List<Data> dataList, OnItemClickListener onItemListClickListener {
this.context = context;
this.dataList = dataList;
this.onItemListClickListener = onItemListClickListener;
}
onCreateViewHolder....
onBindViewHolder....
getItemCount...
class ViewHolderA RecyclerView.ViewHolder {
//instances..
//Constructor...
}
}
类接口A:
interface
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
类接口B:
interface
public interface SingleItemEventListener {
void onSingleItemClicked(int position);
}
类:
FragmentA
在//Instances
private AdapterA adapter;
private RecyclerView recyclerView;
private onSingleItemClicked singleItemEventListener;
onAttach...
onCreateView...
@Override
public void onStart() {
super.onStart();
//Setting adapter
onSetAdapter();
}
private void onSetAdapter() {
List<Data> dataList;
dataList = getData();
adapter = new AdapterA(context, dataList, new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
singleItemEventListener.onSingleItemClicked(position);
}
});
中,我们正在实现Activity
回调以接收事件并使用从接口回调中接收到的数据启动onSingleItemClicked
:
FragmentB
答案 0 :(得分:3)
将ViewModel添加到您的活动中,并使用它在所有组件,活动以及两个片段之间进行通信。
您可以从片段中访问活动的ViewModel
MyViewModel model = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
使用LiveData进行交流,在片段中向其发布操作,并在活动中收听以开始另一个片段。
答案 1 :(得分:0)
您可以仅使用单个界面而不是两个界面来完成同一任务,或者可以在片段和活动之间共享视图模型。
方法1:使用单个界面
将接口定义为
public interface FragmentCallbackListener {
void onItemClick(View view, int position);
}
让您的Activity实现它:
ActivityA extend Activity implements FragmentCallbackListener {
@Override
public void onItemClick(View view, int position) {
....
}
}
附加到实现FragmentCallbackListener的活动的片段应重写片段的onAttach():
public class FragmentA extends Fragment {
private FragmentCallbackListener mListener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the listener. If not, it throws an exception
try {
mListener = (FragmentCallbackListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragmentCallbackListener");
}
}
}
将相同的侦听器实例传递给Adapter:
adapter = new AdapterA(context, dataList, mListener);
方法2:使用共享视图模型
将视图模型定义为:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Data> selected = new MutableLiveData<Data>();
public void select(Data data) {
selected.setValue(data);
}
public LiveData<Data> getSelected() {
return selected;
}
}
FragmentA内部
public class FragmentA extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
...
adapter = new AdapterA(context, datalist, model);
}
}
适配器内部视图onClickListener:
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
model.select(view.getTag()); //or use getData(position) to return selected data
}
});
内部活动A
ActivityA extend Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(this).get(SharedViewModel.class);
model.getSelected().observe(this, data -> {
// launch the new fragment B with selected data
});
}
}
有关更多详细信息,请阅读https://developer.android.com/training/basics/fragments/communicating
答案 2 :(得分:0)
没有外部库,您是否考虑过使用广播?
它仍然需要样板,但根据您的意见可能会更清晰一些(该活动不需要是任何接口的实例,您不需要任何“上帝对象”模型)
在FragmentA的适配器中,单击某个项目后,您将发送带有明确动作和参数作为附加内容的广播。在活动中,您注册(和注销)广播接收器(无需更改清单)。最后,接收方照常启动FragmentB。
片段A /适配器
Intent item = new Intent(MY_CONSTANT);
item.putExtra(MY_EXTRA_CONSTANT, position);
getActivity().sentBroadcast(intent);
活动
private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int position = intent.getIntExtra(MY_EXTRA_CONSTANT, -1);
//TODO: Move your FragmentB transaction here
}
};
void onPause() {
unregisterReceiver(myReceiver);
}
void onResume() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MY_CONSTANT);
registerReceiver(myReceiver, intentFilter);
}
答案 3 :(得分:-2)
您可以不使用 Interface, BroadcastReceiver
和ViewModel
来实现它。
检查以下代码:
ActivityA.java
public class ActivityA extends AppCompatActivity {
ActivityTestBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_test);
pushFragment(new FragmentA());
}
/**
* @param fragment pass fragment you want to replace
*/
public void pushFragment(Fragment fragment) {
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped && manager.findFragmentByTag(backStateName) == null) {
// if fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, fragment, backStateName);
ft.addToBackStack(backStateName);
ft.commit();
}
}
@Override
public void onBackPressed() {
try {
if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
finish();
} else {
getSupportFragmentManager().popBackStack();
}
} catch (Exception e) {
super.onBackPressed();
}
}
}
这里pushFragment
方法适用于所有片段。
在onBackPressed()
中,它将检查BackStackEntryCount。如果计数等于或小于1,则意味着只有最后一个片段在堆栈中,finish();
将被调用并退出应用程序。如果BackStackEntryCount中的计数大于1,则会弹出最后一个片段。
在您的 AdapterA.java 中:
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (context instanceof ActivityA) {
FragmentB mFragmentB = new FragmentB();
Bundle bundle = new Bundle();
bundle.putParcelable("single_data_key", dataList.get(getAdapterPosition()));
mFragmentB.setArguments(bundle);
((ActivityA) context).pushFragment(mFragmentB);
}
}
});
您已经通过了ActivityA的context
。因此,您可以检查if (context instanceof ActivityA)
,然后可以通过使用类型转换来调用ActivityA的pushFragment
方法。
因此,您可以在Android项目中集成的样板代码更少。