我是android studio的新手,我试图实现一个dialogfragment,以便在单击按钮或单击工具栏action_add按钮时弹出。它将数据从对话框发送到片段或活动(试图了解它们如何对两者起作用)。我的对话框类有一个接口,当我在片段中实现它时,该接口工作得很好,但是活动有些不同。我正在使用接口将数据传递给活动,然后正在使用Bundle将数据从活动传递到片段。我相信错误发生在onAttach中,因为我有getTargetFragment();。
是否可以让多个活动/片段实现一个接口?如果是这样,我该如何兼顾接口onAttach和数据发送中的活动和片段?
在此先感谢大家,以下是该对话框的custom_dialog类的代码以及与活动相关的一个片段。目标是按下片段中的按钮或活动上的工具栏以打开对话框并从用户那里获得输入,该输入将被转移以显示。
错误:
Process: com.example.andrewg.dialogfragment, PID: 13335
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.andrewg.dialogfragment.MyCustomDialog$OnInputSelected.sendInput(java.lang.String)' on a null object reference
at com.example.andrewg.dialogfragment.MyCustomDialog$2.onClick(MyCustomDialog.java:58)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25881)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6649)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
MainActivity:
public class MainActivity extends AppCompatActivity implements
MyCustomDialog.OnInputSelected{
public String dialogInput;
FragmentManager fragmentManager;
@Override
public void sendInput(String input) {
dialogInput = input;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu, this adds items to the action bar if it is present
getMenuInflater().inflate(R.menu.menu, menu);
//Redundant
MenuItem actionMenuItem = menu.findItem(R.id.action_add);
actionMenuItem.setOnMenuItemClickListener(new
MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle action bar clicks here. The action bar will automatically handle clicks on the home/up button
//so long as you specify a parent activity in AndroidManifest.xml
switch(item.getItemId()){
case R.id.action_add:
MyCustomDialog dialog = new MyCustomDialog();
dialog.show(getSupportFragmentManager(), "MyCustomDialog");
//Trying to pass dialog input into an intent to send to the
fragment
/*Intent intent = new Intent(getApplicationContext(),
MainFragment.class);
intent.putExtra("Dialog Input", dialogInput);
startActivity(intent);*/
//Trying Bundle to pass data, dialog input between activity and
fragment
Bundle bundle = new Bundle();
bundle.putString("Dialog Input", dialogInput);
//Set Fragment class arguments
MainFragment fragment = new MainFragment();
fragment.setArguments(bundle); //set argument bundle to fragment
fragmentManager.beginTransaction().replace(R.id.MainFragment,fragment).commit(); //now replace Mainfragment
Toast.makeText(this, "Action_Add Clicked Successfully",
Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
}
MainFragment:
public class MainFragment extends Fragment implements
MyCustomDialog.OnInputSelected{
TextView InputDisplay;
Button OpenDialog;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
InputDisplay = view.findViewById(R.id.InputDisplay);
OpenDialog = view.findViewById(R.id.Open_Dialog);
//Getting Main Activity dialog information with Bundle, that was received from toolbar add
Bundle bundle = getArguments();
if(bundle != null){
String dialogInput = bundle.toString();
InputDisplay.setText(dialogInput);
}
//String dialogInput = this.getArguments().getString("Dialog Input");
OpenDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("MainFragment", "onClick: opening dialog");
MyCustomDialog customDialog = new MyCustomDialog();
customDialog.setTargetFragment(MainFragment.this, 1);
customDialog.show(getFragmentManager(), "MyCustomDialog");
}
});
return view;
}
@Override
public void sendInput(String input) {
InputDisplay.setText(input);
}
}
自定义对话框:我为onAttach的活动添加了第二个接口变量,以使用getActivity(),但这似乎不合适。
public class MyCustomDialog extends DialogFragment {
private EditText Input;
private TextView ActionOK, ActionCANCEL;
public OnInputSelected onInputSelected_Fragment;
public OnInputSelected onInputSelected_Activity;
public interface OnInputSelected{
void sendInput(String input);
}
@Override
public void onAttach(Context context) {
try{
onInputSelected_Fragment = (OnInputSelected) getTargetFragment();
onInputSelected_Activity = (OnInputSelected) getActivity();
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
super.onAttach(context);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_my_custom, container, false);
Input = view.findViewById(R.id.Input);
ActionOK = view.findViewById(R.id.Action_OK);
ActionCANCEL = view.findViewById(R.id.Action_CANCEL);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onInputSelected_Fragment.sendInput(Input.getText().toString());
onInputSelected_Activity.sendInput(Input.getText().toString());
getDialog().dismiss();
}
});
return view;
}
}
答案 0 :(得分:1)
是否可以有多个活动/片段实现一个接口?
是的。您只需要知道,如果getActivity()
未附加到null
而是附加到另一个DialogFragment
而Activity
上,则Fragment
将返回getTargetFragment()
如果没有目标null
,将返回Fragment
,例如当您直接从Activity
中显示对话框时,因此没有调用setTargetFragment()
。
由于将null
强制转换为任何值都不会导致Exception
,因此您在代码中要做的就是在实际调用接口方法之前进行null
检查。
但是由于您将没有目标Fragment
和Activity
来显示DialogFragment
的相同实例,因此您可以更改代码并只处理一个字段
private OnInputSelected onInputSelected;
然后在onAttach()
中检查哪个为空-Activity
或目标Fragment
-并一劳永逸地正确设置onInputSelected
。
try{
Fragment onInputSelected_Fragment = getTargetFragment();
Activity onInputSelected_Activity = getActivity();
if (onInputSelected_Fragment != null){
onInputSelected = (OnInputSelected)onInputSelected_Fragment;
}
else {
onInputSelected = (OnInputSelected)onInputSelected_Activity;
}
// throw RuntimeException here if onInputSelected still is null
//
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
请注意,如果从片段显示对话框时忘记设置目标onInputSelected
,则Fragment
可能为空。这将是编程错误,因此在这种情况下,您可能想抛出RuntimeException
。如果您的应用超出了这一点,则说明您已经实现了界面,任务得以完成。
在您的onClick()
中,您只需编写即可
onInputSelected.sendInput(Input.getText().toString());
答案 1 :(得分:0)
getActivity()返回null。根据{{3}},在运行onAttach之后,getActivity()将返回null。如果将super.onAttach()移到onAttach方法的开头,则它应正确返回活动。
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
onInputSelected_Fragment = (OnInputSelected) getTargetFragment();
onInputSelected_Activity = (OnInputSelected) getActivity();
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
}