Use interface implementation in a Fragment

1

I'm trying to implement an interface that I created within fragment , but I'm not having success. My fragment is being defined with the following code:

package fragments;

import pickers.DateTimePickerFragment;
import pickers.DateTimePickerFragment.DateTimeSetListener;
import singletons.SingletonUser;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import br.bytecode.tarefas.R;
import classes.Event;
import database.Database;

public class NewEventFragment extends Fragment implements DateTimeSetListener {

    EditText name, place, date, contact;

    public NewEventFragment() {
    }

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

        setHasOptionsMenu(true);
        View rootView = inflater.inflate(R.layout.fragment_new, container,
                false);

        name = (EditText) rootView.findViewById(R.id.new_input_name);
        place = (EditText) rootView.findViewById(R.id.new_input_place);
        date = (EditText) rootView.findViewById(R.id.new_input_datetime);
        contact = (EditText) rootView.findViewById(R.id.new_input_contact);

        return rootView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        date.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                DateTimePickerFragment dtp = new DateTimePickerFragment();
                dtp.show(getFragmentManager(), "date_time_picker");
                return false;
            }
        });
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_newevent, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        switch (id) {
        case R.id.menu_newevent_save:
            Event event = new Event();

            event.setUserId(SingletonUser.getInstance().getId());
            event.setName(name.getText().toString());
            event.setPlace(place.getText().toString());
            event.setDate(date.getText().toString());
            event.setContact(contact.getText().toString());

            Database db = new Database(getActivity());
            db.addEvent(event);

            Toast.makeText(getActivity(), "Evento criado com sucesso.",
                    Toast.LENGTH_SHORT).show();
            getActivity().finish();

        case R.id.menu_newevent_discard:
            getActivity().finish();
            // getActivity().getFragmentManager().popBackStack();
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onDateTimeSet(int dia, int mes, int ano, int hora, int minuto) {

    }

}

Note in the class declaration of fragment that it implements the interface that I need. However, when I run the program, I get the following error:

06-23 10:57:04.698: E/AndroidRuntime(18295): FATAL EXCEPTION: main
06-23 10:57:04.698: E/AndroidRuntime(18295): Process: br.bytecode.tarefas, PID: 18295
06-23 10:57:04.698: E/AndroidRuntime(18295): java.lang.ClassCastException: activities.ManagementActivity@424699e8 deve implementar DateTimeSetListener
06-23 10:57:04.698: E/AndroidRuntime(18295):    at pickers.DateTimePickerFragment.onAttach(DateTimePickerFragment.java:42)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:849)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1062)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.BackStackRecord.run(BackStackRecord.java:684)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1447)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.os.Handler.handleCallback(Handler.java:733)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.os.Handler.dispatchMessage(Handler.java:95)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.os.Looper.loop(Looper.java:136)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at android.app.ActivityThread.main(ActivityThread.java:5086)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at java.lang.reflect.Method.invokeNative(Native Method)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at java.lang.reflect.Method.invoke(Method.java:515)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
06-23 10:57:04.698: E/AndroidRuntime(18295):    at dalvik.system.NativeStart.main(Native Method)

It is as if the interface had not been implemented at any time within fragment , because the error happens inside the Activity that contains it. Is there any solution for this? How can I make Android understand that the interface is in fragment and not Activity ? The class that needs to interface is this:

package pickers;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.text.format.Time;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.DatePicker;
import android.widget.DatePicker.OnDateChangedListener;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;
import br.bytecode.tarefas.R;

public class DateTimePickerFragment extends DialogFragment {

    Time now = new Time();
    private int dia, mes, ano, hora, minuto;
    private DateTimeSetListener mListener;
    boolean changedTime = false, changedDate = false;

    public DateTimePickerFragment() {
    }

    public interface DateTimeSetListener {
        public void onDateTimeSet(int dia, int mes, int ano, int hora,
                int minuto);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (DateTimeSetListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " deve implementar DateTimeSetListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View rootView = inflater.inflate(R.layout.date_time_picker, null);

        TimePicker tPicker = (TimePicker) rootView
                .findViewById(R.id.timePicker1);
        DatePicker dPicker = (DatePicker) rootView
                .findViewById(R.id.datePicker1);

        now.setToNow();

        dPicker.init(now.year, now.month, now.monthDay,
                new OnDateChangedListener() {

                    @Override
                    public void onDateChanged(DatePicker view, int year,
                            int monthOfYear, int dayOfMonth) {
                        changedDate = true;
                        ano = year;
                        mes = monthOfYear;
                        dia = dayOfMonth;
                    }
                });

        tPicker.setIs24HourView(true);
        tPicker.setOnTimeChangedListener(new OnTimeChangedListener() {

            @Override
            public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
                changedTime = true;
                hora = hourOfDay;
                minuto = minute;
            }
        });

        builder.setView(rootView);
        builder.setTitle("Escolher data e hora");

        builder.setPositiveButton("OK", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                if (mListener != null) {
                    if (changedTime == false) {
                        hora = now.hour;
                        minuto = now.minute;
                    }
                    if (changedDate == false) {
                        dia = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
                        mes = Calendar.getInstance().get(Calendar.MONTH);
                        ano = Calendar.getInstance().get(Calendar.YEAR);
                    }
                    mListener.onDateTimeSet(dia, mes, ano, hora, minuto);
                }
            }
        });
        builder.setNegativeButton("Cancelar", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
        return builder.create();
    }

}
    
asked by anonymous 23.06.2014 / 16:09

1 answer

1
The problem is that when you display DateTimePickerFragment by NewEventFragment , NewEventFragment is never passed as a parameter in onAttach (Due to the Fragment lifecycle, it is compulsorily attached / attached to Activity before being displayed / added to the layout), but the Activity that is supporting the two.

You have two options:

  • The ManagementActivity will be Listener of DateTimePickerFragment . What will involve a refactoring in the communication between Activity and its Fragments .

  • Change how you assign Listener to DateTimePickerFragment , doing it manually (using setter ). So it would look like:

    public class NewEventFragment extends Fragment implements DateTimeSetListener {
        // Código atual
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            date.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    DateTimePickerFragment dtp = new DateTimePickerFragment();
                    dtp.setDateTimeSetListener(NewEventFragment.this);
                    dtp.show(getFragmentManager(), "date_time_picker");
    
                    return false;
                }
            });
    
            super.onActivityCreated(savedInstanceState);
        }
    
        // Codigo atual
    }
    
    
    public class DateTimePickerFragment extends DialogFragment {
        // Código Atual
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            // Remove todo o codigo que tinha aqui
        }
    
        public void setDateTimeSetListener(DateTimeSetListener listener) {
            mListener = listener;
        }
        // Código atual
    }
    
  • 23.06.2014 / 17:27