Challenge : More Dialogs

Here is my solution:

CrimeListFragment.java
package com.bignerdranch.android.criminalintent;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;

public class CrimeListFragment extends Fragment {
    private int clickedCrimePosition;
    private static final String CLICKED_CRIME_POSITION_ID = "clicked_crime_position_id";

    private static final int REQUEST_CRIME = 1;

    private RecyclerView mCrimeRecyclerView;
    private CrimeAdapter mAdapter;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_crime_list, container, false);

        mCrimeRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
        mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        if (savedInstanceState != null) {
            clickedCrimePosition = savedInstanceState.getInt(CLICKED_CRIME_POSITION_ID);
        }

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        updateUI();
    }

    private void updateUI() {
        CrimeLab crimeLab = CrimeLab.get(getActivity());
        List<Crime> crimes = crimeLab.getCrimes();

        if (mAdapter == null) {
            mAdapter = new CrimeAdapter(crimes);
            mCrimeRecyclerView.setAdapter(mAdapter);
        } else {
            mAdapter.notifyItemChanged(clickedCrimePosition);
        }
    }

    private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private TextView mTitleTextView;
        private TextView mDateTextView;
        private ImageView mSolvedImageView;
        private Crime mCrime;

        public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
            super(inflater.inflate(R.layout.list_item_crime, parent, false));
            itemView.setOnClickListener(this);

            mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
            mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
            mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
        }

        public void bind(Crime crime) {
            mCrime = crime;
            mTitleTextView.setText(mCrime.getTitle());
            DateFormat dateFormat = new SimpleDateFormat(CrimeFragment.DATE_FORMAT);
            DateFormat timeFormat = new SimpleDateFormat(CrimeFragment.TIME_FORMAT);
            mDateTextView.setText(dateFormat.format(mCrime.getDate()) + " " + timeFormat.format(mCrime.getTime()));
            mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
        }

        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        @Override
        public void onClick(View v) {
            clickedCrimePosition = getAdapterPosition();
            Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getID());
            startActivityForResult(intent, REQUEST_CRIME);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CRIME) {
            // Handle result
        }
    }

    private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
        private List<Crime> mCrimes;

        public CrimeAdapter(List<Crime> crimes) {
            mCrimes = crimes;
        }

        /**
         * Called when RecyclerView needs a new {@link CrimeHolder} of the given type to represent
         * an item.
         * <p>
         * This new ViewHolder should be constructed with a new View that can represent the items
         * of the given type. You can either create a new View manually or inflate it from an XML
         * layout file.
         * <p>
         * The new ViewHolder will be used to display items of the adapter using
         * {@link #onBindViewHolder(CrimeHolder, int)}. Since it will be re-used to display
         * different items in the data set, it is a good idea to cache references to sub views of
         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
         *
         * @param parent   The ViewGroup into which the new View will be added after it is bound to
         *                 an adapter position.
         * @param viewType The view type of the new View.
         * @return A new ViewHolder that holds a View of the given view type.
         * @see #getItemViewType(int)
         * @see #onBindViewHolder(CrimeHolder, int)
         */
        @Override
        public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
            return new CrimeHolder(layoutInflater, parent);
        }

        /**
         * Called by RecyclerView to display the data at the specified position. This method should
         * update the contents of the {@link CrimeHolder#itemView} to reflect the item at the given
         * position.
         * <p>
         * Note that unlike {@link ListView}, RecyclerView will not call this method
         * again if the position of the item changes in the data set unless the item itself is
         * invalidated or the new position cannot be determined. For this reason, you should only
         * use the <code>position</code> parameter while acquiring the related data item inside
         * this method and should not keep a copy of it. If you need the position of an item later
         * on (e.g. in a click listener), use {@link CrimeHolder#getAdapterPosition()} which will
         * have the updated adapter position.
         * <p>
         * Override {@link #onBindViewHolder(CrimeHolder, int)} instead if Adapter can
         * handle efficient partial bind.
         *
         * @param holder   The ViewHolder which should be updated to represent the contents of the
         *                 item at the given position in the data set.
         * @param position The position of the item within the adapter's data set.
         */
        @Override
        public void onBindViewHolder(CrimeHolder holder, int position) {
            Crime crime = mCrimes.get(position);
            holder.bind(crime);
        }

        /**
         * Returns the total number of items in the data set held by the adapter.
         *
         * @return The total number of items in this adapter.
         */
        @Override
        public int getItemCount() {
            return mCrimes.size();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle onSavedInstanceState) {
        super.onSaveInstanceState(onSavedInstanceState);
        onSavedInstanceState.putSerializable(CLICKED_CRIME_POSITION_ID, clickedCrimePosition);
    }
}
CrimeFragment.java
package com.bignerdranch.android.criminalintent;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import static android.widget.CompoundButton.*;

public class CrimeFragment extends Fragment {

    public static final String DATE_FORMAT = "EEE MMM dd yyyy";
    public static final String TIME_FORMAT = "hh:mm a z";

    private static final String ARG_CRIME_ID = "crime_id";
    private static final String DIALOG_DATE = "DialogDate";
    private static final String DIALOG_TIME = "DialogTime";

    private static final int REQUEST_DATE = 0;
    private static final int REQUEST_TIME = 1;

    private Crime mCrime;
    private EditText mTitleField;
    private Button mDateButton;
    private Button mTimeButton;
    private CheckBox mSolvedCheckBox;

    public static CrimeFragment newInstace(UUID crimeId) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_CRIME_ID, crimeId);

        CrimeFragment fragment = new CrimeFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
        mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_crime, container, false);

        mTitleField = (EditText) v.findViewById(R.id.crime_title);
        mTitleField.setText(mCrime.getTitle());
        mTitleField.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // This space intentionally left blank
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mCrime.setTitle(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {
                // This one too
            }
        });

        mDateButton = (Button) v.findViewById(R.id.crime_date);
        updateDate();
        mDateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager manager = getFragmentManager();
                DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
                dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
                dialog.show(manager, DIALOG_DATE);
            }
        });

        mTimeButton = (Button) v.findViewById(R.id.crime_time);
        updateTime();
        mTimeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager manager = getFragmentManager();
                TimePickerFragment dialog = TimePickerFragment.newInstance(mCrime.getTime());
                dialog.setTargetFragment(CrimeFragment.this, REQUEST_TIME);
                dialog.show(manager, DIALOG_TIME);
            }
        });

        mSolvedCheckBox = (CheckBox) v.findViewById(R.id.crime_solved);
        mSolvedCheckBox.setChecked(mCrime.isSolved());
        mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mCrime.setSolved(isChecked);
            }
        });

        return v;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            return;
        }

        if (requestCode == REQUEST_DATE) {
            Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
            mCrime.setDate(date);
            updateDate();
        }

        if (requestCode == REQUEST_TIME) {
            Date time = (Date) data.getSerializableExtra(TimePickerFragment.EXTRA_TIME);
            mCrime.setTime(time);
            updateTime();
        }
    }

    private void updateDate() {
        DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
        mDateButton.setText(dateFormat.format(mCrime.getDate()));
    }

    private void updateTime() {
        DateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT);
        mTimeButton.setText(timeFormat.format(mCrime.getTime()));
    }
}
Crime.java
package com.bignerdranch.android.criminalintent;

import java.util.Date;
import java.util.UUID;

public class Crime {
    private UUID mID;
    private String mTitle;
    private Date mDate;
    private Date mTime;
    private boolean mSolved;

    public Crime() {
        mID = UUID.randomUUID();
        mDate = new Date();
        mTime = new Date();
    }

    public UUID getID() {
        return mID;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    public Date getDate() {
        return mDate;
    }

    public void setDate(Date date) {
        mDate = date;
    }

    public Date getTime() {
        return mTime;
    }

    public void setTime(Date time) {
        mTime = time;
    }

    public boolean isSolved() {
        return mSolved;
    }

    public void setSolved(boolean solved) {
        mSolved = solved;
    }
}
TimePickerFragment.java
package com.bignerdranch.android.criminalintent;

import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TimePicker;

import java.util.Calendar;
import java.util.Date;

public class TimePickerFragment extends DialogFragment {
    public static final String EXTRA_TIME = "com.bignerdranch.android.criminalintent.time";

    private static final String ARG_TIME = "time";

    private TimePicker mTimePicker;

    public static TimePickerFragment newInstance(Date time) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_TIME, time);

        TimePickerFragment fragment = new TimePickerFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Date time = (Date) getArguments().getSerializable(ARG_TIME);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);

        View v = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_time, null);

        mTimePicker = (TimePicker) v.findViewById(R.id.dialog_time_picker);
        mTimePicker.setCurrentHour(hour);
        mTimePicker.setCurrentMinute(minute);

        return new AlertDialog.Builder(getActivity())
                .setView(v)
                .setTitle(R.string.time_picker_title)
                .setPositiveButton(android.R.string.ok,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                int hour = mTimePicker.getCurrentHour();
                                int minute = mTimePicker.getCurrentMinute();

                                Calendar calendar = Calendar.getInstance();
                                calendar.setTime(time);
                                calendar.set(Calendar.HOUR_OF_DAY, hour);
                                calendar.set(Calendar.MINUTE, minute);
                                Date time = calendar.getTime();
                                sendResult(Activity.RESULT_OK, time);
                            }
                        })
                .create();

    }

    private void sendResult(int resultCode, Date time) {
        if (getTargetFragment() == null) {
            return;
        }

        Intent intent = new Intent();
        intent.putExtra(EXTRA_TIME, time);

        getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
    }
}
fragment_crime.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        style="?android:listSeparatorTextViewStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:text="@string/crime_title_label"/>

    <EditText
        android:id="@+id/crime_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="@string/crime_title_hint"/>

    <TextView
        style="?android:listSeparatorTextViewStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:text="@string/crime_details_label"/>

    <Button
        android:id="@+id/crime_date"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"/>

    <Button
        android:id="@+id/crime_time"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"/>

    <CheckBox
        android:id="@+id/crime_solved"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:text="@string/crime_solved_label"/>

</LinearLayout>
dialog_time.xml
<?xml version="1.0" encoding="utf-8"?>
<TimePicker
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dialog_time_picker"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:timePickerMode="clock">
</TimePicker>
4 Likes

hello,Thank you for your solution。 I tryto your solution,Encounter a problem,Can you help me?Thank you in advance。

This is the error log,05-18 14:13:43.544 14313-14313/com.jkxy.fragment E/AndroidRuntime: FATAL EXCEPTION: main
                                                                   Process: com.jkxy.fragment, PID: 14313
                                                                   java.lang.NullPointerException: Attempt to invoke virtual method 'long java.util.Date.getTime()' on a null object reference
                                                                       at java.util.Calendar.setTime(Calendar.java:1089)
                                                                       at com.jkxy.fragment.TimePickerFragment.onCreateDialog(TimePickerFragment.java:46)
                                                                       at android.support.v4.app.DialogFragment.getLayoutInflater(DialogFragment.java:312)
                                                                       at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1113)
                                                                       at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1295)
                                                                       at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801)
                                                                       at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1682)
                                                                       at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:541)
                                                                       at android.os.Handler.handleCallback(Handler.java:751)
                                                                       at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                       at android.os.Looper.loop(Looper.java:154)
                                                                       at android.app.ActivityThread.main(ActivityThread.java:6077)
                                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
05-18 14:13:43.546 1511-2047/system_process W/ActivityManager:   Force finishing activity com.jkxy.fragment/.CrimePageActivity

Looks like you are calling Date.getTime() on a null object reference as the Stack Trace suggests. Have you tried adding a break point and running in debug mode and watching the value of this object?

Also please paste your code for TimePickerFragment.java

Thanks for your reply,I don‘t know why,my debug button it is black,display is not available,


TimePickerFragment

package com.jkxy.fragment;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TimePicker;
import java.sql.Time;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import static android.content.ContentValues.TAG;
/**

  • Created by 张银帅 on 2017/5/17.

  • 这是一个时间选取的fragment,创建对话框让用户选择具体时间并发送给CrimeFragment
    */

    public class TimePickerFragment extends DialogFragment {
    public static final String EXTRA_TIME = “com.jkxy.fragment.crime.time”;
    private static final String ARG_TIME = “time”;
    private TimePicker mTimePicker;
    //把时间数据保存在TimePickerFragment的arugment bundle中
    public static TimePickerFragment newInstance(Time time) {

     Bundle args = new Bundle();
     args.putSerializable(ARG_TIME,time);
    
     TimePickerFragment fragment = new TimePickerFragment();
     fragment.setArguments(args);
     return fragment;
    

    }
    //创建对话框,
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
    final Date time = (Date) getArguments().getSerializable(ARG_TIME);
    Calendar mCalendar = Calendar.getInstance();//获取到Calendar对象实例
    mCalendar.setTime(time);
    int hour = mCalendar.get(Calendar.HOUR_OF_DAY);
    int minute = mCalendar.get(Calendar.MINUTE);

     //实例化时间选取器视图
     View v = LayoutInflater.from(getActivity())
             .inflate(R.layout.dialog_time, null);
     mTimePicker = (TimePicker) v.findViewById(R.id.dialog_time_time_picker);//找到具体TimePicker视图对象
     mTimePicker.setIs24HourView(false);
     mTimePicker.setCurrentHour(hour);
     mTimePicker.setCurrentMinute(minute);
    
     return new AlertDialog.Builder(getActivity())
             .setView(v)
             .setTitle(R.string.time_picker_title)
             .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                 @Override
                 public void onClick(DialogInterface dialogInterface, int i) {
                     //取得日期和时间,日期不用从TimePicker中获取,但是时间需要
                    int hour = mTimePicker.getCurrentHour();
                     int minute = mTimePicker.getCurrentMinute();
                     Calendar calendar =  Calendar.getInstance();
                     calendar.setTime(time);
                     calendar.set(Calendar.HOUR_OF_DAY,hour);
                     calendar.set(Calendar.MINUTE, minute);
                     Date time = calendar.getTime();
                     sendResult(Activity.RESULT_OK, time);
    
                 }
             })//当用户点击ok时,从DatePicker中获取日期并回传给CrimeFragment
             .create();
    

    }

    private void sendResult(int resultCode, Date time) {
    if (getTargetFragment() == null) {
    return;
    }
    Intent intent = new Intent();
    intent.putExtra(EXTRA_TIME, time);

     getTargetFragment()
             .onActivityResult(getTargetRequestCode(), resultCode, intent);
    

    }

}

I’ve met the same problem.
After debug I still couldn’t find the problem.
So I delete the updateTime() method, and rewrite the updateTime() code where they are needed. Then the problem gone.
after that, I extract the code into updatTime(), the app still works well…

这个我感觉是As的问题,我在debug的时候发现我所有的变量都又赋值啊,不可能存在NUll的情况,然后我又看到他这个给出的提示是Date.getTime()出现的问题,然而在程序中只有在timePickerFragment中使用了Date.getTime()并且这里的Date.getTime()不可能为空,因为calander已经被赋值了。所以我就猜测是不是AS自身的问题,然后我就删除了updateTime()方法,然后在每一个需要updateTime的地方老老实实的写出来那两行代码,然后程序就ok了~
最后用Extract方法生成了updateTime()之后,程序还是OK的~

再补充一句,你需要吧那些getCurrentHour之类的全部换成getHour不然Calander是空的,因为getCurrentHour已经在这个版本的sdk中被移除了。

哈哈。xiaoran,nice to meet you,能在这里看到同胞很开心。我想我和你遇到的不是同一个问题,我没有把updateTime抽到一个方法中,但是还是有问题,

嗯嗯,感谢你的建议。我已经改成getHour了,

I have found the cause of the app crash,


My carelessness:

Don’t need the Time attribute is added in the crime.java.::sweat_smile:

刚刚发现了一个问题,也是偶尔发现的,刚刚把程序拷到一个android版本为4.0的手机中,发现如果用setHour就会崩溃,提示

,于是我换成了getCurrentHour。就能运行了,但是getCurrentHour在android7.0的版本又不能用了/。。

where is it come from? which one you quote?

@wolfheros: I’m not sure I understand your question. If you are asking where the getTime() function is, it is in the Crime.java class file. In my solution I simply used Date objects for storing the date or time

Hi Zhang,

The method getCurrentHour() can be used in android 7.0 even though it’s a deprecated method.
Feel free to use the method. By the way, you can add an if clause to check the build version. It’s a graceful way.

Hi Zhang,

Another tip, you can check the section GENERATING GETTERS AND SETTERS in Chapter 2. There’s a diagram about Setting Java code style preferences. It will make your getters and setters better.

Hi with your code Time is not getting updated anywhere. Is it by default or have I made some mistake?

setCurrentHour() and setCurrentMinute() as well as getCurrentHour() and getCurrentMinute() is deprecated in api level 23 use setHour() , getHour() instead.

There is a bug with date&time. Getting new data from any Picker, we create new objects for date&time storing and then populate mCrime with its content. But when we working with DatePickerFragment, we are losting information about time. Similar problem is with TimePickerFragment. You can see it if you try to change date after time or convercely.
I’ve found two solutions:

  • To create a global Date field either in DatePickerFragment and TimePickerFragment and to populate it with new values, saving other information

  • updating mCrime structure in onActivityResult, construct new Data object combinating old and new data.

This one is quite something. Thanks!

Thank you, very good solution.

But I did not alter the Crime model, no need to add another field for time because mDate stores Time and Date.

mTimeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FragmentManager fragmentManager = getFragmentManager();
            TimePickerFragment dialog = TimePickerFragment.newInstance(mCrime.getDate());
            dialog.setTargetFragment(CrimeFragment.this, REQUEST_TIME);
            dialog.show(fragmentManager, DIALOG_TIME);
        }
    });

And to update time only need to update the Date object

private void updateTime() {
    DateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT, Locale.getDefault());
    mTimeButton.setText(timeFormat.format(mCrime.getDate()));
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) return;

    switch (requestCode) {
        case REQUEST_DATE:
            Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
            mCrime.setDate(date);
            updateDate();
            break;

        case REQUEST_TIME:
            Date time = (Date) data.getSerializableExtra(TimePickerFragment.EXTRA_TIME);
            mCrime.setDate(time);
            updateTime();
            break;
    }
}