如果从Fragment调用,则DialogFragment会抛出ClassCastException

时间:2013-02-01 05:36:46

标签: android fragment android-dialogfragment

如果从Fragment调用,我的DialogFragment将抛出 ClassCastException ,而如果从Activity调用它则正常工作。我已经看过几个有类似问题的其他问题,基本上那些与导入有关,但我在实现中无法解决它。这是我对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");
        }
    }
}

这是我使用上述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);
    }
}

这是错误日志:

  

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必须实现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):在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):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)

我想知道是否有人可以暗示这个问题。

2 个答案:

答案 0 :(得分:12)

来自docs

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

在您的代码中

 mListener = (SetupHotspotDialogListener) activity;

行抛出ClassCastException因为您的活动未实现SetupHotspotDialogListener界面。 (Fragment与包含它的活动直接相关,以及DialogFragment,因为DialogFragment扩展了Fragment)。

再次来自docs

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

因此,如果您想从FragmentDialog创建Fragment,我建议您通过回调来组织活动:

  1. 使用SmartMode等方法在Fragment createDialogRequest()类中创建回调接口(就像使用dialogFragment一样)。
  2. 让您的活动实现该界面
  3. 然后,当您需要创建对话框时,将回复从Fragment发送到Activity
  4. 将“show dialog logics”放入Activity
  5. 看起来像片段询问活动来创建对话框,活动显示对话框。

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

    的活动:

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

    片段:

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

    我使用了支持4个库片段

    android.support.v4.app.Fragment
    android.support.v4.app.DialogFragment
    android.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

       
    <?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>
    

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

答案 1 :(得分:-1)

public class HotspotScanDialog extends DialogFragment {
    ...
    SetupHotspotDialogListener mListener;
    ...
    @Override
    public void onAttachFragment(Fragment childFragment) {
        super.onAttachFragment(childFragment);
        mListener = (SetupHotspotDialogListener) childFragment;
    }
    ...
}

以及在Fragment中调用DialogFragment的

HotspotScanDialog dialog = new HotspotScanDialog();
dialog.show(getChildFragmentManager(), "TAG");