2012-01-28 12 views
10

Sto riscontrando alcuni problemi nell'implementare un TimePicker nella mia applicazione che consente all'utente di modificare l'orario di un record del database prima di inserirlo.Pulsante Android TimePicker AM/PM che non sta invocando onTimeChanged

Il problema è che quando viene premuto il pulsante AM/PM, il metodo onTimeChanged (View, int, int) non viene richiamato. Ogni volta che cambio il valore di ora o minuto di TimePicker, viene chiamato onTimeChanged(), tuttavia.

Scenari:

  • utente clicca semplicemente il pulsante AM/PM: AM/PM è scatta NON aggiornato
  • utente le ore/minuti: ora viene aggiornata scatta
  • utente AM/Il pulsante PM cambia le ore/minuti: Ora e AM/PM vengono aggiornati

Mi sbaglio nel pensare che il pulsante AM/PM possa essere cliccato per aggiornare il pulsante AM/PM tempo senza dover cambiare anche un valore temporale dopo il pulsante am/pm?

Ho messo insieme un piccolo progetto di prova a replicare questo ed ecco il codice:

Attività

public class TestActivity extends Activity implements OnTimeChangedListener { 

    private Calendar mCalendar; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.timepicker); 

     mCalendar = Calendar.getInstance(); 

     TimePicker tp = (TimePicker)findViewById(R.id.timepicker); 
     tp.setIs24HourView(false); 
     tp.setOnTimeChangedListener(this); 
    } 

    @Override 
    public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { 
     Log.d("TAG", "In onTimeChanged"); 
     mCalendar.set(mCalendar.get(Calendar.YEAR), 
         mCalendar.get(Calendar.MONTH), 
         mCalendar.get(Calendar.DAY_OF_MONTH), 
         hourOfDay, 
         minute); 

     setCalendarTime(); 
    } 

    private void setCalendarTime() { 
     Date date = mCalendar.getTime(); 

     if (date != null) { 
      SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy '@' h:mm a"); 
      String dateTime = formatter.format(date); 

      Toast.makeText(this, dateTime, Toast.LENGTH_LONG).show(); 
     } 
    } 
} 

timepicker.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="fill_parent" 
       android:layout_height="fill_parent" 
       android:fillViewport="true"> 
    <TimePicker android:id="@+id/timepicker" 
       android:layout_width="fill_parent" 
       android:layout_height="wrap_content" 
       android:layout_marginLeft="5dip" 
       android:layout_marginRight="5dip"/> 
</LinearLayout> 

risposta

29

Ho testato il codice. Sì, hai ragione, il pulsante TimePicker AM/PM non sta invocando il metodo onTimeChanged che dovrebbe.

In realtà, è un bug. È stato segnalato a google. Potete trovare qui
http://code.google.com/p/android/issues/detail?id=18982

Si prega di votare, commentare & Stella la segnalazione di bug per aumentare la sua priorità e per farlo riparare dal team di sviluppo il più presto possibile.

+0

Grazie per queste informazioni, sam! – hooked82

+4

l'errore è stato segnalato dal 2011 ... bello –

+0

grazie per le informazioni vivik – Jinu

3

this link di spettacoli che viene chiamato il numero onTimeChanged(); che fa scattare th e dispatcher di eventi.

Se non stai ricevendo gli eventi necessari (anche se sembra essere l'invio) si deve avere per

  • estendere il default TimerPicker,

  • esclusione mAmPmButton.setOnClickListener,

  • e includere la versione nella visualizzazione.


mAmPmButton.setOnClickListener(new OnClickListener() { 
     public void onClick(View v) { 
      requestFocus(); 
      if (mIsAm) { 

       // Currently AM switching to PM 
       if (mCurrentHour < 12) { 
        mCurrentHour += 12; 
       }     
      } else { 

       // Currently PM switching to AM 
       if (mCurrentHour >= 12) { 
        mCurrentHour -= 12; 
       } 
      } 
      mIsAm = !mIsAm; 
      mAmPmButton.setText(mIsAm ? mAmText : mPmText); 
      onTimeChanged(); 
     } 
    }); 
+0

Sì, l'ho visto nella fonte. Ho persino estratto il codice sorgente e ricompilato TimePicker/NumberPicker nelle classi locali e funziona come previsto. Il problema con questo è che non voglio dover estrarre la fonte per questi e avere i miei personalizzati. Preferirei capire perché quello predefinito non funziona come previsto nella mia applicazione? – hooked82

+1

Non penso che lo farai funzionare senza fare una di quelle opzioni. Apparentemente c'è un problema con TimerPicker incluso nel sdk. – user123321

3

Si prega di utilizzare il codice qui sotto sta funzionando bene per me.

final TimePicker tp=(TimePicker)findViewById(R.id.timePicker1); 
    View amPmView = ((ViewGroup)tp.getChildAt(0)).getChildAt(2); 
    if(amPmView instanceof Button) 
    { 
     amPmView.setOnClickListener(new OnClickListener() { 

      @Override 
      public void onClick(View v) { 
       Log.d("OnClickListener", "OnClickListener called"); 
       if(v instanceof Button) 
       { 
        if(((Button) v).getText().equals("AM")) 
        { 
         ((Button) v).setText("PM"); 
         if (tp.getCurrentHour() < 12) { 
          tp.setCurrentHour(tp.getCurrentHour() + 12); 
          } 

        } 
        else{ 
         ((Button) v).setText("AM"); 
         if (tp.getCurrentHour() >= 12) { 
          tp.setCurrentHour(tp.getCurrentHour() - 12); 
          } 
        } 
       } 

      } 
     }); 
    } 
+0

Grazie, funziona benissimo. Ho commentato le righe v.setText ("AM/PM") e viene comunque aggiornato in modo appropriato. Non sono sicuro se questo è b/c tp.setCurrentHour() finisce per sparare l'evento timeChanged. – samosaris

+0

Ho un problema con questo approccio, l'instanceof check non funziona. –

6

Ho trovato questa risposta, ma non ha funzionato per me, quindi ho pensato di aggiornare.

In 3.2 (e sopra?), Il Time Picker non ha un pulsante per am/pm. Invece ha un NumberPicker. Il seguente codice ha funzionato per me. L'ultimo bit è lì perché avevo bisogno di limitare il tempo selezionato a non prima della data 'min' e non superiore alla data 'max', quindi dovevo controllare la data selezionata nel controllo DatePicker corrispondente:

 NumberPicker amPmView = (NumberPicker)((ViewGroup)tp.getChildAt(0)).getChildAt(3); 
     amPmView.setOnValueChangedListener(new OnValueChangeListener() { 
      @Override 
      public void onValueChange(NumberPicker arg0, int arg1, int arg2) { 
       if(arg0.getValue()== 1){ 
        if (tp.getCurrentHour() < 12) 
         tp.setCurrentHour(tp.getCurrentHour() + 12); 
       } 
       else{ 
        if (tp.getCurrentHour() >= 12) 
         tp.setCurrentHour(tp.getCurrentHour() - 12); 
       } 

       int year = dp.getYear(); 
       int month = dp.getMonth(); 
       int dayOfMonth = dp.getDayOfMonth(); 
       int hourOfDay = tp.getCurrentHour(); 
       int minute = tp.getCurrentMinute(); 

       if (year == min.get(Calendar.YEAR) && month == min.get(Calendar.MONTH) && dayOfMonth == min.get(Calendar.DAY_OF_MONTH)){ 
        if ((hourOfDay < min.get(Calendar.HOUR_OF_DAY))|| 
         (hourOfDay == min.get(Calendar.HOUR_OF_DAY) && (minute < min.get(Calendar.MINUTE)))){ 
         tp.setCurrentHour(min.get(Calendar.HOUR_OF_DAY)); 
         tp.setCurrentMinute(min.get(Calendar.MINUTE)); 
        } 
       }else if (year == max.get(Calendar.YEAR) && month == max.get(Calendar.MONTH) && dayOfMonth == max.get(Calendar.DAY_OF_MONTH)){ 
        if ((hourOfDay > max.get(Calendar.HOUR_OF_DAY))|| 
         (hourOfDay == max.get(Calendar.HOUR_OF_DAY) && (minute > max.get(Calendar.MINUTE)))){ 
         tp.setCurrentHour(max.get(Calendar.HOUR_OF_DAY)); 
         tp.setCurrentMinute(max.get(Calendar.MINUTE)); 
        } 
       } 
      } 
     }); 
0

Ecco un rapido progetto che ho riunito che supporta Android 4.0+. Fondamentalmente, mostra un TextView che è sempre sincronizzato con TimePicker, indipendentemente da quale spinner verticale viene manipolato dall'utente.

https://gist.github.com/danialgoodwin/5694256

0

ho rendere questo metodo i thats Restituisce vero se l'TimePicker selezionare AM e false se l'TimePicker selezionare PM. nota: il suppresLint è per Api 8 non funziona .getvalue() istruzione

Saludos !! :)

@SuppressLint("NewApi") 
private boolean isAM(TimePicker timePicker){ 
    NumberPicker numberPickerAmPm = (NumberPicker)((ViewGroup) timePicker.getChildAt(0)).getChildAt(3); 
    if(numberPickerAmPm.getValue()==0){ 
     //sendToast("es am"); 
     return true; 
    }else{ 
     //sendToast("es pm"); 
     return false; 
    } 

} 
0

Vorrei condividere anche la mia soluzione dato che gli altri rimedi qui pubblicati non funzionano per me. Ma sono in grado di capirlo. Questa riga:

View amPmView = ((ViewGroup)tp.getChildAt(0)).getChildAt(2); 

Sembra non restituire il selettore per AM_PM.

NumberPicker amPmView = (NumberPicker ((ViewGroup)tp.getChildAt(0)).getChildAt(3); 

Né questo pure, dal momento che questo restituisce un TextView. Sembra che sia l'etichetta designata sul selettore.

Ma utilizzando quest'ultimo, ottenere il quarto elemento figlio restituisce il selettore per AM_PM. Ecco cosa ho ottenuto finora:

NumberPicker amPmView = (NumberPicker)((ViewGroup) mTimePicker.getChildAt(0)).getChildAt(4); 
    amPmView.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() 
    { 
     @Override 
     public void onValueChange(NumberPicker picker, int oldVal, int newVal) 
     { 
      Log.i(NoteApplication.TAG, "AM_PM selected..."); 
     } 
    }); 

Ora sono in grado di rilevare le modifiche in AM_PM. Spero che questo aiuti alcune altre persone che non possono recuperarlo tramite getChildAt(2) o getChildAt(3) come descritto da altre risposte.

Anche prendere nota che questo è il caso per la classe TimePicker, non ho ancora provato questo su un TimePickerDialog quindi non sono sicuro per quello. Sto verificando al min sdk 8 mira api 22.

HTH

3

avuto lo stesso problema quando si crea il TimePicker nel codice e nessuna delle soluzioni fornite lavorato per me. Di seguito è stato quello che ho finito per fare. Testato su livelli API 18, 21 e 23

final TimePicker tp = new TimePicker(getContext()); 
tp.setOnTimeChangedListener(this); 
try { 
    ViewGroup amPmView; 
    ViewGroup v1 = (ViewGroup)tp.getChildAt(0); 
    ViewGroup v2 = (ViewGroup)v1.getChildAt(0); 
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 
     ViewGroup v3 = (ViewGroup)v2.getChildAt(0); 
     amPmView = (ViewGroup)v3.getChildAt(3); 
    } else { 
     amPmView = (ViewGroup)v2.getChildAt(3); 
    } 
    View.OnClickListener listener = new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      tp.setCurrentHour((tp.getCurrentHour() + 12) % 24); 
     } 
    }; 
    View am = amPmView.getChildAt(0); 
    View pm = amPmView.getChildAt(1); 
    am.setOnClickListener(listener); 
    pm.setOnClickListener(listener); 
} catch (Exception e) { 
    // DO nothing... just ignore the workaround if this fails. 
} 
+0

questo snippet è riuscito a mettere il listener su AM/PM ma ora l'AM/PM non funziona come hai sostituito il listener predefinito dal listener. Mi raccomando –

+0

Ho sostituito onClickListener con OnTouch per non ignorare l'ascoltatore di clic originale –

+1

Ciao @Hisham, L'intera idea di questa soluzione alternativa è di correggere il listener predefinito in quanto ha un bug come rilevato da Vivek. Il listener predefinito non chiama il callback onTimeChanged, quindi dovrai sostituirlo con il listener personalizzato e chiamare tp.setCurrentHour ((tp.getCurrentHour() + 12)% 24); per garantire che la richiamata venga attivata al momento giusto. – velval

4

soluzione Velval è l'unica soluzione che funziona e compatiple con differenti vesrions Android. L'ho provato su api 17 e 23. ma ha un problema che impedisce l'ascolto originale su Click su Android 6. quindi ecco cosa ha funzionato con me. di usare tatto, invece di clic:

public class MyTimePicker extends TimePicker { 

    private OnTimeChangedListener onTimeChangedListener; 

    public MyTimePicker(Context context) { 
     super(context); 
     // init(); 
    } 

    public MyTimePicker(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     //init(); 
    } 

    public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     // init(); 
    } 

    @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
    public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
     super(context, attrs, defStyleAttr, defStyleRes); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     // Stop ScrollView from getting involved once you interact with the View 
     if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 
      ViewParent p = getParent(); 
      if (p != null) 
       p.requestDisallowInterceptTouchEvent(true); 
     } 
     return false; 
    } 

    @Override 
    public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 
     super.setOnTimeChangedListener(onTimeChangedListener); 
     this.onTimeChangedListener = onTimeChangedListener; 
    } 

    @Override 
    protected void onFinishInflate() { 
     super.onFinishInflate(); 
     init(); 
    } 

    private void init() { 

     try { 
      ViewGroup amPmView; 
      ViewGroup v1 = (ViewGroup) getChildAt(0); 
      ViewGroup v2 = (ViewGroup) v1.getChildAt(0); 
      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 
       ViewGroup v3 = (ViewGroup) v2.getChildAt(0); 
       amPmView = (ViewGroup) v3.getChildAt(3); 
      } else { 
       amPmView = (ViewGroup) v2.getChildAt(3); 
      } 
      View.OnTouchListener listener = new View.OnTouchListener() { 
       @Override 
       public boolean onTouch(View v, MotionEvent event) { 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 
         onTimeChangedListener.onTimeChanged(MyTimePicker.this, getCurrentHour(), getCurrentMinute()); 
        } else { 
         int hour = getCurrentHour(); 
         if (hour >= 12) { 
          hour -= 12; 
         } else { 
          hour += 12; 
         } 
         onTimeChangedListener.onTimeChanged(MyTimePicker.this, hour, getCurrentMinute()); 
        } 

        return false; 
       } 

      }; 
      View am = amPmView.getChildAt(0); 
      View pm = amPmView.getChildAt(1); 

      am.setOnTouchListener(listener); 
      pm.setOnTouchListener(listener); 
     } catch (Exception e) { 
      // DO nothing... just ignore the workaround if this fails. 
     } 


    } 
} 
+0

Questa è una buona soluzione, mentre Google ha risolto il problema; che potrebbe non essere in qualunque momento presto. – duke

0

Ho anche affrontato lo stesso problema utilizzando API 21, e il problema non è stato ancora risolto.

Ho sostituito la vista TimePicker per un TimePickerDialog e ha funzionato.

Il codice seguente imposta TextView con il tempo selezionato da TimePickerDialog. È possibile sostituire il tv.setText() con qualsiasi logica:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    final TextView tv = (TextView)findViewById(R.id.textView); 

    Calendar calendar = Calendar.getInstance(); 
    int hour = calendar.get(Calendar.HOUR_OF_DAY); 
    int minute = calendar.get(Calendar.MINUTE); 
    TimePickerDialog timePickerDialog = new TimePickerDialog(this, 
      new TimePickerDialog.OnTimeSetListener() { 
       @Override 
       public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 
        tv.setText(String.format(Locale.getDefault(),"Hour: %d, Minute: %d ",hourOfDay,minute)); 
       } 
    }, hour, minute, true); 
    timePickerDialog.show(); 
} 
1

Con API 25 Torrone, il codice di Velval/Hisham è l'unico che funziona, ma non in modalità orizzontale. Ho cambiato il codice e funziona bene :):

public class AMPMTimePicker extends TimePicker { 

private static final String TAG = "AMPMTimePicker"; 
private OnTimeChangedListener onTimeChangedListener; 

public AMPMTimePicker(Context context) { 
    super(context); 
} 

public AMPMTimePicker(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

public AMPMTimePicker(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); 
} 

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
public AMPMTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
    super(context, attrs, defStyleAttr, defStyleRes); 
} 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    // Stop ScrollView from getting involved once you interact with the View 
    if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 
     ViewParent p = getParent(); 
     if (p != null) 
      p.requestDisallowInterceptTouchEvent(true); 
    } 
    return false; 
} 

@Override 
public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 
    super.setOnTimeChangedListener(onTimeChangedListener); 
    this.onTimeChangedListener = onTimeChangedListener; 
} 

@Override 
protected void onFinishInflate() { 
    super.onFinishInflate(); 
    init(); 
} 

@SuppressWarnings("deprecation") 
private void init() { 
    try { 
     ViewGroup amPmView; 

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 

      // LinearLayout (LOLLIPOP) 
      // GridLayout (M-LANDSCAPE) 
      // LinearLayout (M-PORTRAIT) 
      ViewGroup v1 = (ViewGroup) getChildAt(0); 

      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 

       // FrameLayout (LOLLIPOP-LANDSCAPE) 
       // FrameLayout - id:time_header (LOLLIPOP-PORTRAIT) 
       ViewGroup v2 = (ViewGroup) v1.getChildAt(0); 

       // FrameLayout - id:TimeHeader (LOLLIPOP-LANDSCAPE) 
       // LinearLayout (LOLLIPOP-PORTRAIT) 
       ViewGroup v3 = (ViewGroup) v2.getChildAt(0); 

       if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 
        ViewGroup v4 = (ViewGroup) v3.getChildAt(0); // LinearLayout (LOLLIPOP) 
        amPmView = (ViewGroup) v4.getChildAt(3); // LinearLayout - id:ampm_layout (LOLLIPOP) 
       } else { // PORTRAIT 
        amPmView = (ViewGroup) v3.getChildAt(3); // LinearLayout - id:ampm_layout (LOLLIPOP) 
       } 
      } else { // M and after 
       if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 
        ViewGroup v2 = (ViewGroup) v1.getChildAt(1); // RelativeLayout (M) 
        amPmView = (ViewGroup) v2.getChildAt(1); // LinearLayout - id:ampm_layout (M) 
       } else { 
        ViewGroup v2 = (ViewGroup) v1.getChildAt(0); // RelativeLayout - id:time_header (M) 
        amPmView = (ViewGroup) v2.getChildAt(3); // LinearLayout - id:ampm_layout (M) 
       } 
      } 

      View am = amPmView.getChildAt(0); // AppCompatCheckedTextView - id:am_label 
      View pm = amPmView.getChildAt(1); // AppCompatCheckedTextView - id:pm_label 

      View.OnTouchListener listener = new View.OnTouchListener() { 
       @Override 
       public boolean onTouch(View v, MotionEvent event) { 
        int hour; 
        int minute; 
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 
         hour = getCurrentHour(); 
         minute = getCurrentMinute(); 
        } else { 
         hour = getHour(); 
         minute = getMinute(); 
        } 
        hour = (hour >= 12) ? hour - 12 : hour + 12; 
        onTimeChangedListener.onTimeChanged(AMPMTimePicker.this, hour, minute); 
        return false; 
       } 
      }; 
      am.setOnTouchListener(listener); 
      pm.setOnTouchListener(listener); 
     } 
    } catch (Exception e) { 
     Log.e(TAG, "TimePicker is not defined for this Android version : " + e.getMessage()); 
    } 
} 
} 
Problemi correlati