布局TimePicker为基础的对话preference布局、基础、TimePicker、preference

2023-09-07 02:39:17 作者:无敌欢喜哥

我正在写一个应用程序,需要一个TimePicker为基础preference(二,实际上)和我一些刷卡(Apache许可)code从的 Android的编程教程的书。我已经修改了它的工作好一点,但有一件事我百思不得其解。

I'm writing an app that requires a TimePicker-based preference (two, actually) and I swiped some (Apache-licensed) code from the Android Programming Tutorials book. I've modified it to work a little better, but there's one thing puzzling me.

我还没有尝试过实际设备还在,但在模拟器现在考虑到系统设置为24小时制时间显示,清除重点TimePicker对话框,使手工编辑使用的时候,而不是时间甚至阅读箭头键,和 - 有问题的部分 - 显示在preferenceScreen所选时间

I haven't tried it on an actual device yet but on emulators it now takes into account system settings for 24-hour time display, clears focus on the TimePicker dialog so that the time is read even when edited manually instead of using the arrow keys, and - the problematic part - displays the selected time in the PreferenceScreen.

现在的问题是,我已经在仿真器API级别3,4,7和10尝试这样做,它只能完全正确10.在仿真器的早期版本,如果我把一个复选框在preferenceScreen有两个时间preferences,在这两个时间preferences的TextViews每次复选框被触发时开关位置。所以,如果我点击复选框的第一次应用程序启动,启动时间貌似晚上10点停止时的样子上午8时(即使当我点击了preference,在适当的时候出现在了TimePicker)。

The problem is that I've tried this in emulators for API levels 3, 4, 7 and 10 and it only works completely correctly in 10. In the emulators for the earlier versions, if I put a checkbox in the PreferenceScreen with two TimePreferences, the TextViews in the two TimePreferences switch positions every time the checkbox is toggled. So if I click the checkbox the first time the app starts, the start time looks like 10pm and the stop time looks like 8am (even though when I click on the preference, the appropriate time shows up in the TimePicker).

有没有更好的方式来做到这一点? preferably一种优雅的方式,使整个类是独立的,我并不需要建立在那些只适用于类XML文件的布局?或者是有办法解决/解决这个问题?我也想避免使用setSummary,这样还是有机会的总结添加到preference。我的大部分实验已经内部onCreateView的布局code,但我不能得到任何工作完全正确。这似乎发生我是否使用RelativeLayout的还是LinearLayout中。

Is there a better way to do this? Preferably an elegant way so that the entire class is self-contained and I don't need to create layouts in XML files that are applicable only to the class? Or is there a way to work around/fix this behavior? I'd also like to avoid using setSummary so that there's still opportunity to add a summary to the preference. Most of my experimentation has been inside onCreateView with the layout code but I can't get anything to work completely right. This seems to happen whether I use RelativeLayout or LinearLayout.

满code是如下:

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]);;
    }
}

这是例子preferences布局如下:

An example preferences layout is below:

<?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>

截图:

这发生在仿真器的API 3,4和7。我也试图在10和它工作正常。

This happens on emulators for APIs 3, 4 and 7. I've also tried it on 10 and it works correctly.

更新:它正常工作与API 8以及(2.2 /升级Froyo)的模拟器

Update: It works correctly on an emulator with API 8 as well (2.2/Froyo).

更新2:我已经重写了 onCreateView 方法如下。它仍然无法以同样的方式,但它更有效地渲染,我相信它的表现更接近Android的文件中规定的要求。

Update 2: I've rewritten the onCreateView method as follows. It still fails the same way, but it's more efficient to render and I believe it behaves closer to the requirements specified in the Android documentation.

    @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一些日志记录,看看他们是如何调用,有什么意见内部发生的。如果有人想出了在此期间的任何想法,请让我知道!

My next step is to start some logging in onCreateView, onBindView and getView and see how they're called and what's going on inside the views. If anyone comes up with any ideas in the meantime, please let me know!

推荐答案

我已经想通了,并固定它。它现在在从蛋糕的API的每一个层次(1.5 - 3级)为姜饼(2.3.3 - 10级)。 code是如下。请注意,原来的code是Apache许可中的问题说明。在此,我奉献我所有的修改到公共领域。

I've figured it out and fixed it. It now works in every level of the API from Cupcake (1.5 - level 3) to Gingerbread (2.3.3 - level 10). Code is below. Please note that the original code is Apache-licensed as specified in the question. I hereby dedicate all of my modifications to the public domain.

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]);;
    }
}