Макет для DialogPreference на основе TimePicker - PullRequest
0 голосов
/ 21 июня 2011

Я пишу приложение, для которого требуются предпочтения на основе TimePicker (на самом деле два), и я пролистал некоторый (лицензированный Apache) код из Руководства по программированию для Android . Я изменил его, чтобы он работал немного лучше, но есть одна вещь, которая меня озадачивает.

Я еще не пробовал его на реальном устройстве, но в эмуляторах теперь он учитывает системные настройки для 24-часового отображения времени, очищает фокус от диалогового окна TimePicker, так что время считывается даже при редактировании вручную вместо использования клавиши со стрелками и - проблемная часть - отображают выбранное время на PreferenceScreen.

Проблема в том, что я пробовал это в эмуляторах для API уровней 3, 4, 7 и 10, и он работает полностью корректно только в 10. В эмуляторах для более ранних версий, если я установил флажок в PreferenceScreen с две TimePreferences, TextViews в двух TimePreferences переключают позиции каждый раз, когда флажок установлен. Поэтому, если я установлю флажок при первом запуске приложения, время запуска будет выглядеть как 22:00, а время остановки - как 8:00 (хотя, когда я нажимаю на предпочтение, соответствующее время отображается в TimePicker).

Есть ли лучший способ сделать это? Предпочтительно элегантный способ, чтобы весь класс был автономным, и мне не нужно создавать макеты в файлах XML, которые применимы только к классу? Или есть способ обойти / исправить это поведение? Я также хотел бы избежать использования setSummary, чтобы все еще была возможность добавить сводку к предпочтению. Большая часть моих экспериментов была внутри onCreateView с кодом макета, но я не могу заставить что-либо работать полностью правильно. Похоже, это происходит независимо от того, использую ли я RelativeLayout или LinearLayout.

Полный код выглядит следующим образом:

package test.android.testproject2;

//imports...

public class TimePreference extends DialogPreference {
    private int lastHour=0;
    private int lastMinute=0;
    private boolean is24HourFormat;
    private TimePicker picker=null;
    private TextView timeDisplay;

    public TimePreference(Context ctxt) {
        this(ctxt, null);
    }

    public TimePreference(Context ctxt, AttributeSet attrs) {
        this(ctxt, attrs, 0);
    }

    public TimePreference(Context ctxt, AttributeSet attrs, int defStyle) {
        super(ctxt, attrs, defStyle);

        is24HourFormat = DateFormat.is24HourFormat(ctxt);
        setPositiveButtonText("Set");
        setNegativeButtonText("Cancel");
    }

    @Override
    public String toString() {
        if(is24HourFormat) {
            return ((lastHour < 10) ? "0" : "")
                    + Integer.toString(lastHour)
                    + ":" + ((lastMinute < 10) ? "0" : "")
                    + Integer.toString(lastMinute);
        } else {
            int myHour = lastHour % 12;
            return ((myHour == 0) ? "12" : ((myHour < 10) ? "0" : "") + Integer.toString(myHour))
                    + ":" + ((lastMinute < 10) ? "0" : "") 
                    + Integer.toString(lastMinute) 
                    + ((lastHour >= 12) ? " PM" : " AM");
        }
    }

    @Override
    protected View onCreateDialogView() {
        picker=new TimePicker(getContext().getApplicationContext());
        return(picker);
    }

    @Override
    protected void onBindDialogView(View v) {
        super.onBindDialogView(v);
        picker.setIs24HourView(is24HourFormat);
        picker.setCurrentHour(lastHour);
        picker.setCurrentMinute(lastMinute);
    }

    @Override
    protected View onCreateView (ViewGroup parent) {
         View prefView = super.onCreateView(parent);
         LinearLayout layout = new LinearLayout(parent.getContext());
         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT, 2);
         layout.addView(prefView, lp);
         timeDisplay = new TextView(parent.getContext());
         timeDisplay.setGravity(Gravity.BOTTOM | Gravity.RIGHT);
         timeDisplay.setText(toString());
         LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT, 1);
         layout.addView(timeDisplay, lp2);
         return layout;
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);

        if (positiveResult) {
            picker.clearFocus();
            lastHour=picker.getCurrentHour();
            lastMinute=picker.getCurrentMinute();

            String time=String.valueOf(lastHour)+":"+String.valueOf(lastMinute);

            if (callChangeListener(time)) {
                persistString(time);
                timeDisplay.setText(toString());
            }
        }
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        return(a.getString(index));
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        String time=null;

        if (restoreValue) {
            if (defaultValue==null) {
                time=getPersistedString("00:00");
            }
            else {
                time=getPersistedString(defaultValue.toString());
            }
        }
        else {
            if (defaultValue==null) {
                time="00:00";
            }
            else {
                time=defaultValue.toString();
            }
            if (shouldPersist()) {
                persistString(time);
            }
        }

        String[] timeParts=time.split(":");
        lastHour=Integer.parseInt(timeParts[0]);
        lastMinute=Integer.parseInt(timeParts[1]);;
    }
}

Пример макета настроек приведен ниже:

<?xml version="1.0" encoding="UTF-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="@string/preferences_time_title">
        <test.android.testproject2.TimePreference
            android:key="preferences_start_time"
            android:showDefault="true"
            android:defaultValue="08:00"
            android:summary="When to start"
            android:title="@string/preferences_start_time"/>
        <test.android.testproject2.TimePreference
            android:key="preferences_stop_time"
            android:showDefault="true"
            android:defaultValue="22:00"
            android:summary="When to stop"
            android:title="@string/preferences_stop_time"/>
    </PreferenceCategory>
    <PreferenceCategory android:title="@string/preferences_options_title">
        <CheckBoxPreference
            android:key="preferences_enabled"
            android:defaultValue="false"
            android:title="@string/preferences_enabled"/>
    </PreferenceCategory>
</PreferenceScreen>

Скриншоты:

Default settings

Checked settings

Это происходит на эмуляторах для API 3, 4 и 7. Я также пробовал на 10, и он работает правильно.

Обновление: Также корректно работает на эмуляторе с API 8 (2.2 / Froyo).

Обновление 2: Я переписал метод onCreateView следующим образом. По-прежнему происходит сбой таким же образом, но он более эффективен для рендеринга, и я считаю, что он ведет себя ближе к требованиям, указанным в документации Android.

    @Override
    protected View onCreateView (ViewGroup parent) {
         ViewGroup prefView = (ViewGroup) super.onCreateView(parent);
         View widgetLayout;
         int childCounter = 0;
         do {
             widgetLayout = prefView.getChildAt(childCounter);
             childCounter++;
         } while (widgetLayout.getId() != android.R.id.widget_frame); 
         timeDisplay = new TextView(widgetLayout.getContext());
         timeDisplay.setText(toString());
         ((ViewGroup) widgetLayout).addView(timeDisplay);
         return prefView;
    }

Мой следующий шаг - начать вход в onCreateView, onBindView и getView и посмотреть, как они вызываются и что происходит внутри представлений. Если у кого-нибудь появятся какие-то идеи, пожалуйста, дайте мне знать!

1 Ответ

3 голосов
/ 23 июня 2011

Я понял это и исправил это. Теперь он работает на каждом уровне API, от Cupcake (1,5 - уровень 3) до Gingerbread (2.3.3 - уровень 10). Код ниже. Обратите внимание, что оригинальный код Apache-лицензирован, как указано в вопросе. Настоящим я посвящаю все свои модификации в общественное достояние.

package test.android.testproject2;

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.TimePicker;

public class TimePreference extends DialogPreference {
    protected int lastHour=0;
    protected int lastMinute=0;
    protected boolean is24HourFormat;
    protected TimePicker picker=null;
    protected TextView timeDisplay;

    public TimePreference(Context ctxt) {
        this(ctxt, null);
    }

    public TimePreference(Context ctxt, AttributeSet attrs) {
        this(ctxt, attrs, 0);
    }

    public TimePreference(Context ctxt, AttributeSet attrs, int defStyle) {
        super(ctxt, attrs, defStyle);

        is24HourFormat = DateFormat.is24HourFormat(ctxt);
        setPositiveButtonText("Set");
        setNegativeButtonText("Cancel");
    }

    @Override
    public String toString() {
        if(is24HourFormat) {
            return ((lastHour < 10) ? "0" : "")
                    + Integer.toString(lastHour)
                    + ":" + ((lastMinute < 10) ? "0" : "")
                    + Integer.toString(lastMinute);
        } else {
            int myHour = lastHour % 12;
            return ((myHour == 0) ? "12" : ((myHour < 10) ? "0" : "") + Integer.toString(myHour))
                    + ":" + ((lastMinute < 10) ? "0" : "") 
                    + Integer.toString(lastMinute) 
                    + ((lastHour >= 12) ? " PM" : " AM");
        }
    }

    @Override
    protected View onCreateDialogView() {
        picker=new TimePicker(getContext().getApplicationContext());
        return(picker);
    }

    @Override
    protected void onBindDialogView(View v) {
        super.onBindDialogView(v);
        picker.setIs24HourView(is24HourFormat);
        picker.setCurrentHour(lastHour);
        picker.setCurrentMinute(lastMinute);
    }

    @Override
    public void onBindView(View view) {
        View widgetLayout;
        int childCounter = 0;
        do {
            widgetLayout = ((ViewGroup) view).getChildAt(childCounter);
            childCounter++;
        } while (widgetLayout.getId() != android.R.id.widget_frame); 
        ((ViewGroup) widgetLayout).removeAllViews();
        timeDisplay = new TextView(widgetLayout.getContext());
        timeDisplay.setText(toString());
        ((ViewGroup) widgetLayout).addView(timeDisplay);
        super.onBindView(view);
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);

        if (positiveResult) {
            picker.clearFocus();
            lastHour=picker.getCurrentHour();
            lastMinute=picker.getCurrentMinute();

            String time=String.valueOf(lastHour)+":"+String.valueOf(lastMinute);

            if (callChangeListener(time)) {
                persistString(time);
                timeDisplay.setText(toString());
            }
        }
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        return(a.getString(index));
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        String time=null;

        if (restoreValue) {
            if (defaultValue==null) {
                time=getPersistedString("00:00");
            }
            else {
                time=getPersistedString(defaultValue.toString());
            }
        }
        else {
            if (defaultValue==null) {
                time="00:00";
            }
            else {
                time=defaultValue.toString();
            }
            if (shouldPersist()) {
                persistString(time);
            }
        }

        String[] timeParts=time.split(":");
        lastHour=Integer.parseInt(timeParts[0]);
        lastMinute=Integer.parseInt(timeParts[1]);;
    }
}
...