前言:
本篇文章讲解的是 Activity 与 DialogFragment 如何传递数据进行通信,在项目中经常要用
到,然后自己在做项目的过程中,梳理了一下,基本可以当做模板来使用。供参考
基本原理:
Activity 向 Fragment 传递数据: 在Activity中创建Bundle数据包,并调用Fragment的
setArguments(Bundle bundle) 方法即可将Bundle数据包传给Fragment。
Fragment 向Activity传递数据: 在Fragment中定义一个内部回调接口,在让包含该Fragment的Activity实现该回调接口,这样子Fragment即可调用该回调方法将数据传给Activity。
Activity代码如下:
1. 实现 Fragment中的 自定义CallBackListener接口
2. 通过setArguments(bundle)方法传递数据给Fragment
public class MainActivity extends AppCompatActivity implements CustomFragment.CallBackListener {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.test_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CustomFragment customfragment = new CustomFragment();
Bundle bundle = new Bundle();
bundle.putString("transfer_data", "Activity 传递给 Fragment数据");
//通过setArguments(bundle)方法传递数据
customfragment.setArguments(bundle);
customfragment.show(getSupportFragmentManager(), "test");
}
});
}
//接收从Fragment 返回给Activity 的数据
@Override
public void returnFragmentData(String message) {
Log.e("test", "打印 Actvity data : " + message);
}
}
DialogFragment 的代码,我这里做一个父类封装,子类只需要复写加载自定义的layout文件的方法即可,然后UI控件的事件也可以在里面处理
1. 父类DialogFragment的代码
public abstract class CustomDialogFragment extends DialogFragment {
private static final String TAG = "test";
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new AlertDialog.Builder(getActivity())
.setView(getCustomView())
.create();
return dialog;
}
//只需要复写这个方法即可,处理事件也在这个方法中
protected abstract View getCustomView();
@Override
public void onStart() {
super.onStart();
//在横竖屏情况下设置dialog对话框的宽和高, 设置DialogFragment的宽和高,需要在
//onCreateDialog 生命周期方法之后才能设置成功
Configuration newConfig = getResources().getConfiguration();
Dialog dialog = getDialog();
if (dialog != null) {
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
//横屏和竖屏的宽高
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
dialog.getWindow().setLayout((int) (dm.widthPixels*0.4), ViewGroup.LayoutParams.WRAP_CONTENT);
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
dialog.getWindow().setLayout((int) (dm.widthPixels*0.64), ViewGroup.LayoutParams.WRAP_CONTENT);
}
//点击dialog外部,不关闭
dialog.setCanceledOnTouchOutside(false);
}
}
@Override
public void onCancel(DialogInterface dialog) {
dialogFinsih();
}
private void dialogFinsih(){
try {
getActivity().finish();
} catch (Exception e) {
Log.e(TAG, "father activity is destoryed "+ e);
}
}
}
在使用DiaglogFragment 有三个注意的地方:
第一点: 设置Dialog的宽和高的时候,一定要在生命周期onCreateDialog() 方法之后才可以设置成功,这里自定义宽高我写在onStart()方法中
第二点: dialog.getWindow().setLayout((int) (dm.widthPixels*0.4), ViewGroup.LayoutParams.WRAP_CONTENT) ,通过查看源码,setLayout()的宽高参数,实质是这个Dialog的 DecorView 的宽高, 显示出来的Dialog宽高要比你设置的宽高要小一点,所以UI适配的时候,要适当去调整一下,比如UI给的设计图Dialog 的宽度是400dp, 你设置这个参数的时候需要设置大一点(估计432dp左右),宽高都是可以通过Layout inspector工具去查看,然后适配调整
/**
* Set the width and height layout parameters of the window. The default
* for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
* or an absolute value to make a window that is not full-screen.
*
* @param width The desired layout width of the window.
* @param height The desired layout height of the window.
*
* @see ViewGroup.LayoutParams#height
* @see ViewGroup.LayoutParams#width
*/
public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
dispatchWindowAttributesChanged(attrs);
}
#最终调用的是 Dialog.java 中的onWindowAttributesChanged 方法,是给mDecor设置宽高
@Override
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
if (mDecor != null) {
mWindowManager.updateViewLayout(mDecor, params);
}
}
Dialog设置宽高参数效果图:
Dialog呈现给用户宽高效果图:
第三点:
如果你的DialogFragment,导入包为:android.app.DialogFragment;
那么显示的时候调用的方法为:
xxxxx.show(getFragmentManager(), "testFragment");
如果你的DialogFragment 导入包为 :androidx.fragment.app.DialogFragment
那么显示的时候调用的方法为:
xxxxx.show(getSupportFragmentManager(), "testFragment");
2. 子类DialogFragment的代码
public class CustomFragment extends CustomDialogFragment {
private CallBackListener mFragmentListener;
public interface CallBackListener {
//回调方法
void returnFragmentData(String message);
}
//当该Fragent被添加显示到Activity时,回调该方法
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try{
//把该activity当成 实现CallBackListener接口的对象
mFragmentListener = (CallBackListener)activity;
}catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implementon CallBackListener");
}
}
//当该Fragment 从它所属的activity 中被移除的时候,回调此方法
@Override
public void onDetach() {
super.onDetach();
//将 mFragmentListener 置为空
mFragmentListener = null;
}
//设置dialogFragment 的自定义布局 ,然后获取子View 设置响应事件
@Override
protected View getCustomView() {
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_main, null);
Button button = view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//子View的响应事件
if (null != mFragmentListener) {
mFragmentListener.returnFragmentData("从 Fragment 返回给 Actvity的数据");
}
}
});
return view;
}
//接收从Activity传递过来的数据
@Override
public void onResume() {
super.onResume();
Bundle receiveBundler = getArguments();
String data = receiveBundler.getString("transfer_data");
Log.e("test", " 打印 fragment data: " + data);
}
}
代码解读:
第一点:子类DialogFragment 只需要复写 getCustomView() 方法, 实现设置dialogFragment 的自定义布局 ,然后获取子View 设置响应事件
第二点: 定义一个内部回调接口CallBackListener ,在onAttach方法中把 Activity转换成实现CallBackListener接口的对象,然后在需要的地方调用。这样子就把DialogFragment中的数据传递到Activity中了。
总结:
Activity 与 Fragment 的通信基本是一个固定模板,在项目使用中根据需求再稍微调整即可。