【问题标题】:TimePicker onTimeSet not being calledTimePicker onTimeSet 未被调用
【发布时间】:2013-04-10 01:15:36
【问题描述】:

我正在使用时间选择器让用户输入他想要的时间来执行特定任务,我正在使用支持库中提供的 DialogFragment 类来向后兼容旧的 Android 版本。

这是我创建 TimePickerFragment 类的代码,在一个单独的文件中创建,取自:http://developer.android.com/guide/topics/ui/controls/pickers.html

package com.calls.only;
import java.util.Calendar;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.widget.TimePicker;


public class TimePickerFragment extends DialogFragment
                            implements TimePickerDialog.OnTimeSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute, false);
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }
}

主要活动:

package com.calls.only;


import java.util.Calendar;
import java.util.TimeZone;
import android.os.Bundle;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.DialogFragment;
import android.view.Menu;
import android.view.View;
import android.widget.RadioButton;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {

public void InputStartTime(View v) {
    DialogFragment newFragment = new TimePickerFragment();
    newFragment.show(getSupportFragmentManager(), "timePicker");

}

private TimePickerDialog.OnTimeSetListener mTimeSetListener =
        new TimePickerDialog.OnTimeSetListener() {
                    //Overriding onTimeSet causes an error, see below
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
               Log.i("TimePicker", "Time picker set!");
            }
        };

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}
}

从日志中可以看出,没有调用 onTimeset 方法,如果我尝试覆盖此方法,则会收到错误消息: “new TimePickerDialog.OnTimeSetListener(){} 类型的方法 onTimeSet(TimePicker, int, int) 必须覆盖超类方法”

谁能告诉我问题是什么? 我一直在想办法弄明白,这让我感到非常沮丧!

【问题讨论】:

  • 我测试了代码,它与 @override 添加在 onTimeSet 上方的 TimePickerFragment 类中正常工作。 MainActivity 中的侦听器将永远不会被调用,因为您没有在任何地方使用它。您可以随时在TimePickerFragment 类中创建自己的接口,将时间发送到MainActivity

标签: java android datepicker overriding timepicker


【解决方案1】:

从日志中可以看出,onTimeset 方法没有被调用

嗯,那是因为当您创建 TimePickerDialog 时,您将片段提供为 OnTimeSetListener

return new TimePickerDialog(getActivity(), this, hour, minute, false);
                                             ^

换句话说:您没有看到日志语句,因为您的活动中的 mTimeSetListener 变量永远不会被设置为您在片段中创建的对话框的侦听器。

您可以通过将片段附加到您的MainActivity 的活动转换为您的MainActivity 来轻松解决此问题,或者,如果您更喜欢可重用的东西,可以通过接口回调它。在这种情况下,您可以重用OnTimeSetListener 接口,但您也可以设置自己的接口,例如,将Calendar 对象而不是原始小时/分钟值传递回活动。

在它最基本的形式中,它看起来有点像这样:

public class TimePickerFragment extends DialogFragment {

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

        if (!(getActivity() instanceof OnTimeSetListener)) throw new IllegalStateException("Activity should implement OnTimeSetListener!");
        OnTimeSetListener timeSetListener =  (OnTimeSetListener) getActivity();

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), timeSetListener, hour, minute, false);
    }
}

然后让您的 MainActivity 实现相同的接口,而不是使用匿名内部类:

public class MainActivity extends FragmentActivity implements TimePickerDialog.OnTimeSetListener { 

    @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        Log.i("TimePicker", "Time picker set!");
    }
}

更新:如 cmets 中所述,要启用对多个选择器的支持,您有多种选择。一是跟踪托管活动中显示的对话框;如果您只需要区分两个选择器,布尔值就可以了,否则枚举将是实现两个以上状态的合适方法。您需要确保在配置更改期间保留此信息...

但是,我更喜欢做的是能够通过为每个选择器提供一个 id 来识别onTimeSet(...) 返回的结果的来源。然后将该 id 包含在结果中,以便我们知道它来自哪里。我将在下面概述总体思路:

public class TimePickerFragment extends DialogFragment implements OnTimeSetListener {

    private int mId;
    private TimePickerDialogListener mListener;

    private static TimePickerFragment newInstance(int id) {
        Bundle args = new Bundle();
        args.putInt("picker_id", id);
        TimePickerFragment fragment = new TimePickerFragment();
        fragment.setArguments(args);
        return fragment;
    }

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

        mId = getArguments().getInt("picker_id");
        mListener = getActivity() instanceof TimePickerDialogListener ? (TimePickerDialogListener) getActivity() : null;

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute, false);
    }

    @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        if (mListener != null) mListener.onTimeSet(mId, view, hourOfDay, minute);
    }

    public static interface TimePickerDialogListener {
        public void onTimeSet(int id, TimePicker view, int hourOfDay, int minute);
    }

}

我们在上面所做的更改是将对话框本身注册为OnTimeSetListener,然后它将通过TimePickerDialogListener 传递数据,前提是托管活动实现了该接口。所以这就是我们下一步需要做的。另外,请注意,我添加了一个静态便捷方法来创建一个新的TimePickerFragment,它需要一个 id。该值将设置为片段的参数,确保它成为片段状态的一部分,因此您不必自己担心配置更改 - 框架会为您完成。

所以让我们更改MainActivity 来实现我们的自定义接口,并让它使用newInstance 方法:

public class MainActivity extends FragmentActivity implements TimePickerFragment.TimePickerDialogListener { 

    private static final int START_TIME_PICKER_ID = 1;
    private static final int END_TIME_PICKER_ID = 2;

    public void InputStartTime(View v) {
        // supply the appropriate id - I'm assuming you'll be adding an InputEndTime method somewhere that will then supply END_TIME_PICKER_ID 
        DialogFragment newFragment = TimePickerFragment.newInstance(START_TIME_PICKER_ID);
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }

    @Override public void onTimeSet(int id, TimePicker view, int hourOfDay, int minute) {
        Log.i("TimePicker", "Time picker set from id " + id + "!");

        // here you can compare the id value to figure out what picker this data came from
    }
}

最后一点:我是直接在浏览器中输入的,所以请注意任何明显的拼写错误。

【讨论】:

  • 非常感谢!有效,尽管@Override 仍然产生错误,删除它正常工作,也感谢您的解释,救了我的命!
  • 由于我的应用程序中有两个时间选择器,我如何区分它们?上面的代码检测到这些时间选择器之一的 onTimeSet,如何扩展它以支持多个选择器?你能提供这方面的指导吗?谢谢!
  • @naz89:如果您是针对 Java 1.5 构建的,那么在实现接口方法时,@Override 注释可能会产生错误。这在 Java 1.6 中有所改变。支持多个选择器的方法不止一种:您可以在实例化对话框时提供某种“id”,并设置您自己的包含此 id 的接口回调。或者,只需根据显示的选择器在您的活动中切换一个布尔值并检查其在onTimeSet(...) 中的值。
  • 我尝试以与这些答案相同的方式将 id 提供给 onCreateDialog 方法:stackoverflow.com/questions/3734981/… 我尝试了第一个答案,但我遇到了很多错误,我得到了相同的 Override 错误但在里面方法,你能用代码解释一下我应该怎么做吗?再次感谢!
  • @naz89:我不会使用您链接到的答案中的代码,因为这基本上是“旧”的做事方式。这就是不推荐使用onCreateDialog() 的原因,您可能已经注意到了。相反,请查看我上面答案中的编辑。希望这一切都有意义。让我知道你过得怎么样!
【解决方案2】:

我让代码使用它。

public class TimePickerFragment extends DialogFragment{
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), (MainActivity)getActivity(), hour, minute, false);
    }
}


public class MainActivity extends FragmentActivity  implements TimePickerDialog.OnTimeSetListener{

    public void InputStartTime(View v) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");

    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // TODO Auto-generated method stub
        Log.i("TimePicker", "Time picker set!");
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

【讨论】:

  • 非常感谢!有效,尽管@Override 仍然产生错误,删除它正常工作,也感谢您的解释,救了我的命!
  • 由于我的应用程序中有两个时间选择器,我如何区分它们?上面的代码检测到这些时间选择器之一的 onTimeSet,如何扩展它以支持多个选择器?你能提供这方面的指导吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-07
相关资源
最近更新 更多