如果从 Fragment 调用,则 DialogFragment 会抛出 ClassCastException [英] DialogFragment throws ClassCastException if called from Fragment

查看:45
本文介绍了如果从 Fragment 调用,则 DialogFragment 会抛出 ClassCastException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果从 Fragment 调用,我的 DialogFragment 会抛出 ClassCastException,而如果从 Activity 调用,它会正常工作.我已经查看了一些其他具有类似问题的问题,基本上这些问题与导入有关,但我无法在我的实现中解决它.这是我对 DialogFragment 的实现.

<前>导入 android.app.AlertDialog;导入 android.app.Dialog;导入 android.app.DialogFragment;公共类 HotspotScanDialog 扩展了 DialogFragment {SetupHotspotDialogListener mListener;@覆盖公共对话框 onCreateDialog(Bundle savedInstanceState) {....setAdapter(hotspotAdapter, new DialogInterface.OnClickListener() {@覆盖public void onClick(DialogInterface dialog, int which) {mListener.onHotspotSelectedListener(hotspotAdapter.getItem(which).toString());}})...}公共接口 SetupHotspotDialogListener {public void onHotspotSelectedListener(String selection);}@覆盖public void onAttach(活动活动){super.onAttach(活动);尝试 {mListener = (SetupHotspotDialogListener) 活动;} catch (ClassCastException 忽略) {//只是为了确定是否有人会指着我的投掷//ClassCastException 我自己也试过没有这个代码.抛出新的 ClassCastException(activity.toString()+ " 必须实现 NoticeDialogListener");}}}

这是我使用上述 DialogFragment 的 Fragment:

<前>导入 android.app.AlertDialog;导入 android.app.DialogFragment;导入 android.support.v4.app.Fragment;导入 com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog;导入 com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog.SetupHotspotDialogListener;公共类 SmartMode 扩展 Fragment 实现 SetupHotspotDialogListener {私人无效 showWifiSelectionDialog() {DialogFragment setupWifiSelectionDialog = new HotspotScanDialog();/** 使用 getFragmentManager() 只说方法* DialogFragment 类型中的 show(FragmentManager, String) 不是* 适用于参数 (FragmentManager, String)"*/setupWifiSelectionDialog.show(getActivity().getFragmentManager(),Keys.TAG.toString());}@覆盖public void onHotspotSelectedListener(String selection) {//Log.d(TAG,selection);}}

这是错误日志:

<块引用>

02-01 13:11:32.750:E/AndroidRuntime(15061):致命异常:主要02-01 13:11:32.750: E/AndroidRuntime(15061): java.lang.ClassCastException: com.milanix.tuki.UiMainActivity@41d75350 必须实现 NoticeDialogListener02-01 13:11:32.750: E/AndroidRuntime(15061): 在 com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog.onAttach(HotspotScanDialog.java:122)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.FragmentManagerImpl.moveToState(FragmentManager.java:787)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1035)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.BackStackRecord.run(BackStackRecord.java:635)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1397)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.FragmentManagerImpl$1.run(FragmentManager.java:426)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.os.Handler.handleCallback(Handler.java:615)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.os.Handler.dispatchMessage(Handler.java:92)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.os.Looper.loop(Looper.java:137)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 android.app.ActivityThread.main(ActivityThread.java:4898)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 java.lang.reflect.Method.invokeNative(Native Method)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 java.lang.reflect.Method.invoke(Method.java:511)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)02-01 13:11:32.750: E/AndroidRuntime(15061): 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)02-01 13:11:32.750: E/AndroidRuntime(15061): at dalvik.system.NativeStart.main(Native Method)

我想知道是否有人可以就这个问题给出提示.

解决方案

来自 文档:

 onAttach(Activity) 一旦片段与其活动相关联,就会调用.

在你的代码中

 mListener = (SetupHotspotDialogListener) 活动;

line 抛出 ClassCastException 因为您的 Activity 没有实现 SetupHotspotDialogListener 接口.(Fragment 与包含它的 Activity 以及 DialogFragment 直接相关,因为 DialogFragment 扩展了 Fragment).

再次来自文档

在某些情况下,您可能需要一个片段来与活动共享事件.一个很好的方法是在片段内定义一个回调接口,并要求宿主 Activity 实现它.当 Activity 通过接口收到回调时,它可以根据需要与布局中的其他片段共享信息.

所以如果你想从 Fragment 创建 FragmentDialog 我建议通过回调来组织它:

  1. 使用 createDialogRequest() 等一种方法在 SmartMode Fragment 类中创建回调接口(就像在 dialogFragment 中所做的一样).
  2. 让您的 Activity 实现该接口
  3. 然后,当您需要创建对话框时,将回调从Fragment 发送到Activity
  4. 将显示对话逻辑"放入Activity

看起来像片段询问活动来创建对话框,活动显示对话框.

我想我已经找到了你需要的更好的实现.我写了一个简单的例子,从片段创建 fragment dialog 并将 fragment dialog 回调事件接收到片段中.

活动:

public class MyFragmentActivity extends FragmentActivity {@覆盖protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_my_fragment);//活动的第一次开始如果(savedInstanceState == null){//将片段放入活动布局MyFragment 片段 = new MyFragment();FragmentTransaction ft = getSupportFragmentManager().beginTransaction();ft.replace(R.id.fragmentContainer,片段,片段");ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);ft.commit();}}}

片段:

public class MyFragment extends Fragment 实现 MyDialogInterface {@覆盖公共视图 onCreateView(LayoutInflater inflater,ViewGroup 容器,Bundle savedInstanceState) {super.onCreateView(inflater, container, savedInstanceState);View fragmentView = inflater.inflate(R.layout.fragment, null);//显示对话框的按钮按钮 showDialogBu​​tton = (Button) fragmentView.findViewById(R.id.showDialog);showDialogBu​​tton.setOnClickListener(new OnClickListener() {@覆盖public void onClick(View v) {//创建片段对话框.FragmentDialog 对话框 = FragmentDialog.getInstance(MyFragment.this);dialog.show(getActivity().getSupportFragmentManager(), "dialog");}});返回片段视图;}@覆盖公共无效 onClickEvent() {//从对话框片段接收点击事件Log.e(getClass().getSimpleName(), "onClickEvent");}}

片段对话框:

public class FragmentDialog extends DialogFragment {公共接口 MyDialogInterface 扩展 Serializable {公共无效 onClickEvent();}私有 MyDialogInterface 回调监听器;/*** dialogInterface - 将处理的 MyDialogInterface 的实例* 回调事件*/公共静态 FragmentDialog getInstance(MyDialogInterface dialogInterface) {FragmentDialog fragmentDialog = new FragmentDialog();//设置片段参数Bundle args = new Bundle();args.putSerializable("dialogInterface", dialogInterface);fragmentDialog.setArguments(args);返回片段对话框;}@覆盖public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setStyle(STYLE_NO_TITLE, 0);}@覆盖公共视图 onCreateView(LayoutInflater inflater,ViewGroup 容器,Bundle savedInstanceState) {查看 pushDialogView = getActivity().getLayoutInflater().inflate(R.layout.fragment_dialog, null);//从参数中获取对 MyDialogInterface 实例的引用callbackListener = (MyDialogInterface) getArguments().getSerializable("dialogInterface");按钮取消按钮 = (按钮) pushDialogView.findViewById(R.id.button);cancelButton.setOnClickListener(new OnClickListener() {@覆盖public void onClick(View v) {//发送点击事件callbackListener.onClickEvent();}});返回 pushDialogView;}}

我使用了支持 4 个库片段

android.support.v4.app.Fragmentandroid.support.v4.app.DialogFragmentandroid.support.v4.app.FragmentActivity

和布局:

activity_my_fragment.xml :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/fragmentContainer"android:layout_width="match_parent"android:layout_height="match_parent"/>

fragment.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"机器人:背景=#a00"机器人:方向=垂直"><按钮android:id="@+id/showDialog"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="show doalog"/></LinearLayout>

fragment_dialog.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"机器人:背景=#fe3"机器人:方向=垂直"><按钮android:id="@+id/按钮"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击我"/></LinearLayout>

这个想法是发送对接口的引用,它将捕获回调事件.

My DialogFragment throws ClassCastException if called from Fragment, while it is working normally if called from an Activity. I have already looked at few other questions with similar problem and basically those are related to imports, but I haven't been able to solve it in my implementation. Here is my implementation for DialogFragment.

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;

public class HotspotScanDialog extends DialogFragment {

    SetupHotspotDialogListener mListener;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ...

        .setAdapter(hotspotAdapter, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mListener.onHotspotSelectedListener(hotspotAdapter.getItem(
                        which).toString());
            }
        })...
    }

    public interface SetupHotspotDialogListener {
        public void onHotspotSelectedListener(String selection);

    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mListener = (SetupHotspotDialogListener) activity;
        } catch (ClassCastException ignore) {
            // Just to make sure if anyone will be pointing at my throwing
            // ClassCastException myself I have tried without this code as well.
            throw new ClassCastException(activity.toString()
                    + " must implement NoticeDialogListener");
        }
    }
}

Here is my Fragment that is using the above DialogFragment:

import android.app.AlertDialog;
import android.app.DialogFragment;
import android.support.v4.app.Fragment;
import com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog;
import com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog.SetupHotspotDialogListener;

public class SmartMode extends Fragment implements SetupHotspotDialogListener {

    private void showWifiSelectionDialog() {
        DialogFragment setupWifiSelectionDialog = new HotspotScanDialog();

        /*
         * using getFragmentManager() only says "The method
         * show(FragmentManager, String) in the type DialogFragment is not
         * applicable for the arguments (FragmentManager, String)"
         */

        setupWifiSelectionDialog.show(getActivity().getFragmentManager(),
                Keys.TAG.toString());
    }

    @Override
    public void onHotspotSelectedListener(String selection) {
        // Log.d(TAG,selection);
    }
}

This is the error log:

02-01 13:11:32.750: E/AndroidRuntime(15061): FATAL EXCEPTION: main 02-01 13:11:32.750: E/AndroidRuntime(15061): java.lang.ClassCastException: com.milanix.tuki.UiMainActivity@41d75350 must implement NoticeDialogListener 02-01 13:11:32.750: E/AndroidRuntime(15061): at com.xxx.yyy.ui.compontent.dialog.HotspotScanDialog.onAttach(HotspotScanDialog.java:122) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:787) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1035) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.BackStackRecord.run(BackStackRecord.java:635) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1397) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.FragmentManagerImpl$1.run(FragmentManager.java:426) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.os.Handler.handleCallback(Handler.java:615) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.os.Handler.dispatchMessage(Handler.java:92) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.os.Looper.loop(Looper.java:137) 02-01 13:11:32.750: E/AndroidRuntime(15061): at android.app.ActivityThread.main(ActivityThread.java:4898) 02-01 13:11:32.750: E/AndroidRuntime(15061): at java.lang.reflect.Method.invokeNative(Native Method) 02-01 13:11:32.750: E/AndroidRuntime(15061): at java.lang.reflect.Method.invoke(Method.java:511) 02-01 13:11:32.750: E/AndroidRuntime(15061): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006) 02-01 13:11:32.750: E/AndroidRuntime(15061): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773) 02-01 13:11:32.750: E/AndroidRuntime(15061): at dalvik.system.NativeStart.main(Native Method)

I am wondering if anyone can give a hint about this issue.

解决方案

From docs:

 onAttach(Activity) called once the fragment is associated with its activity.

In your code

 mListener = (SetupHotspotDialogListener) activity;

line throws ClassCastException because your activity don't implement SetupHotspotDialogListener interface. (Fragment is directly tied to the activity that contains it, as well as DialogFragment because DialogFragment extends Fragment).

Again from docs

In some cases, you might need a fragment to share events with the activity. A good way to do that is to define a callback interface inside the fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary.

So if you want to create FragmentDialog from Fragment I suggest to organize it via callbacks to activity:

  1. create callback interface into your SmartMode Fragment class (like you do into dialogFragment) with one method like createDialogRequest().
  2. let your activity implement that interface
  3. then, when you need to create dialog, send callback from Fragment to Activity
  4. place "show dialog logics" into Activity

It's look like fragment ask activity to create dialog, activity shows dialog.

EDITED: I think i had found better implementation of what you need. I've write a simple example of creating fragment dialog from fragment with receiving fragment dialog callback events into fragment.

Activity:

public class MyFragmentActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_fragment);

        // first start of activity
        if (savedInstanceState == null) {
            // put fragment to activity layout 
            MyFragment fragment = new MyFragment();
            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.replace(R.id.fragmentContainer, fragment, "fragment");
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            ft.commit();
        }
    }

}

Fragment:

public class MyFragment extends Fragment implements MyDialogInterface {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        View fragmentView = inflater.inflate(R.layout.fragment, null);

        // button which shows dialog
        Button showDialogButton = (Button) fragmentView.findViewById(R.id.showDialog);
        showDialogButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // create fragment dialog.
                FragmentDialog dialog = FragmentDialog.getInstance(MyFragment.this);
                dialog.show(getActivity().getSupportFragmentManager(), "dialog");
            }
        });

        return fragmentView;
    }

    @Override
    public void onClickEvent() {
        // receive click events from dialog fragment
        Log.e(getClass().getSimpleName(), "onClickEvent");
    }

}

FragmentDialog:

public class FragmentDialog extends DialogFragment {

    public interface MyDialogInterface extends Serializable {
        public void onClickEvent();
    }

    private MyDialogInterface callbackListener;

    /**
     * dialogInterface - instance of MyDialogInterface which will handle
     * callback events
     */
    public static FragmentDialog getInstance(MyDialogInterface dialogInterface) {
        FragmentDialog fragmentDialog = new FragmentDialog();

        // set fragment arguments
        Bundle args = new Bundle();
        args.putSerializable("dialogInterface", dialogInterface);
        fragmentDialog.setArguments(args);

        return fragmentDialog;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View pushDialogView = getActivity().getLayoutInflater().inflate(R.layout.fragment_dialog, null);

        // get reference to MyDialogInterface instance from arguments
        callbackListener = (MyDialogInterface) getArguments().getSerializable("dialogInterface");

        Button cancelButton = (Button) pushDialogView.findViewById(R.id.button);
        cancelButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // send click event
                callbackListener.onClickEvent();
            }
        });

        return pushDialogView;
    }

}

I used support 4 library fragments

android.support.v4.app.Fragment
android.support.v4.app.DialogFragment
android.support.v4.app.FragmentActivity

And layouts:

activity_my_fragment.xml :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/fragmentContainer"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

fragment.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#a00"
   android:orientation="vertical" >

   <Button
     android:id="@+id/showDialog"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="show doalog" />
</LinearLayout>

fragment_dialog.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#fe3"
   android:orientation="vertical" >

   <Button
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="click me" />
 </LinearLayout>

The idea is to send reference to interface which will catch callback events.

这篇关于如果从 Fragment 调用,则 DialogFragment 会抛出 ClassCastException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆