根据从片段A收集的数据以更少的样板代码从活动启动片段B

时间:2018-07-12 17:09:08

标签: java android android-fragments android-activity interface

我有以下设置:

  1. 我有一个Activity启动了FragmentA
    FragmentA包含一个recyclerView和一个adapter

  2. 我在interfaceA中实现的适配器中有一个FragmentA,以便通知我单击了哪个位置。

  3. 我在interfaceB中创建了第二个FragmentA,该Activity在步骤1中启动FragmentA的{​​{1}}中实现。
  4. 最后,我要根据从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

4 个答案:

答案 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, BroadcastReceiverViewModel来实现它。
检查以下代码:

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项目中集成的样板代码更少。