DialogFragment выбрасывает ClassCastException при вызове из фрагмента

Мой DialogFragment выбрасывает ClassCastException при вызове из фрагмента, в то время как он работает нормально, если вызывается из действия. Я уже рассмотрел несколько других вопросов с аналогичной проблемой, и в основном они связаны с импортом, но я не смог решить ее в своей реализации. Вот моя реализация 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): FATAL EXCEPTION: main 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): в 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 / AndroidR untime (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 (собственный метод) 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): в dalvik.system.NativeStart.main (собственный метод)

Мне интересно, может ли кто-нибудь намекнуть по этой проблеме.


person Milan    schedule 01.02.2013    source источник


Ответы (1)


Из документов:

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

В вашем коде

 mListener = (SetupHotspotDialogListener) activity;

строка выдает ClassCastException, потому что ваша деятельность не реализует SetupHotspotDialogListener интерфейс. (Fragment напрямую привязан к активности, которая его содержит, а также DialogFragment, потому что DialogFragment расширяет Fragment).

Снова из документации

В некоторых случаях вам может понадобиться фрагмент, чтобы поделиться событиями с действием. Хороший способ сделать это - определить интерфейс обратного вызова внутри фрагмента и потребовать, чтобы активность хоста реализовывала его. Когда действие получает обратный вызов через интерфейс, оно может при необходимости делиться информацией с другими фрагментами макета.

Поэтому, если вы хотите создать FragmentDialog из Fragment, я предлагаю организовать это через обратные вызовы к активности:

  1. создайте интерфейс обратного вызова в вашем классе SmartMode Fragment (как вы делаете это в dialogFragment) с помощью одного метода, например createDialogRequest().
  2. пусть ваша деятельность реализует этот интерфейс
  3. затем, когда вам нужно создать диалог, отправьте обратный вызов с Fragment на Activity
  4. поместите "показать логику диалога" в Activity

Похоже, что фрагмент запрашивает активность для создания диалога, активность показывает диалог.

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>

Идея состоит в том, чтобы отправить ссылку на интерфейс, который будет улавливать события обратного вызова.

person ashakirov    schedule 01.02.2013
comment
Если вы внимательно посмотрите на мой код, он ясно говорит, что открытый класс SmartMode extends Fragment реализует SetupHotspotDialogListener. Этот код отлично работает с активностью, а также реализован интерфейс обратного вызова. Вы имеете в виду, что я должен реализовать интерфейс как основное действие, на котором размещены эти фрагменты? - person Milan; 01.02.2013
comment
Это было не совсем то, что я думал, но реализация этого интерфейса в действии, в котором размещены эти фрагменты, решила мою проблему. Хотя это может быть ограничено, если вы пытаетесь поместить список диалогов в фактический фрагмент, который вызвал этот диалог. В любом случае спасибо за подсказку. - person Milan; 02.02.2013
comment
Я отредактировал свой ответ. Думаю, первая часть моего ответа - не лучшее решение. - person ashakirov; 04.02.2013