【问题标题】:Custom dialog interface for multiple Activities/ Fragments多个活动/片段的自定义对话框界面
【发布时间】:2018-11-29 20:48:19
【问题描述】:

我是 android studio 的新手,我正在尝试实现一个对话框片段,以便在单击按钮或单击工具栏 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)

主活动:

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);
}
}

主片段:

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;
}
}

【问题讨论】:

  • 请一次只问一个问题。单独回答它们更容易。未来的读者将有更好的机会准确地找到他们正在寻找的东西。我的建议:问一个关于你的第二个问题的新问题,因为大部分文本/代码 sn-p 属于第一个问题。
  • 好吧,我编辑它只是为了解决我的主要问题
  • NPE 不会出现在 onAttach() 中,因为您可以在 Logcat 中读取“at com.example.andrewg.dialogfragment.MyCustomDialog$2.onClick(MyCustomDialog.java:58)”。所以它发生在某些onClick() 方法的执行过程中。第 58 行是哪一行?

标签: java android android-studio android-fragments dialog


【解决方案1】:

是否可以让多个活动/片段实现一个接口?

是的。您只需要注意getActivity() 将返回null 如果DialogFragment 未附加到Activity 而是附加到另一个FragmentgetTargetFragment() 将返回null 如果没有目标@987654328 @,例如当您直接从您的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());
}

请注意,如果您在显示片段中的对话框时忘记设置目标FragmentonInputSelected 可能为空。这将是一个编程错误,所以在这种情况下,您可能想抛出一个RuntimeException。如果您的应用通过了这一点,那么您就有了实现接口的东西,任务就完成了。

在你的onClick()你可以简单地写

onInputSelected.sendInput(Input.getText().toString());

【讨论】:

  • 感谢 nosugar,效果很好。你能看看我是如何在 MainFragment 中检索我的包的吗?它显示为“Bundle[{Dialog Input = hello}],其中 hello 是输入。它只会在我第二次喜欢操作栏时更新。谢谢
【解决方案2】:

getActivity() 返回 null。根据the answer here,getActivity() 将返回 null,直到运行 onAttach 之后。如果您将 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());
    }

}

【讨论】:

    猜你喜欢
    • 2016-11-03
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    • 1970-01-01
    • 2017-02-16
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    相关资源
    最近更新 更多