2010-12-01 7 views
2

私は30日間の月/カレンダー(Outlookカレンダーと考える)のように見える日付選択ツールを作成しています。日付ピッカーには、個々の日のTableViewであるMonthViewビューのListView(スクロール用)が含まれています。 MonthViewの個々の日はボタンです。 MonthViewがインスタンス化されるとき、私は日のそれぞれを歩く(ボタン)、クリックリスナーを添付:Android:ListView内のボタンはonClickイベントを受け取りません

final Button b = getButtonAt(i); 
b.setOnClickListener(new OnClickListener() 
{ 
     @Override 
     public void onClick(View v) { 
      setSelectedDate(buttonDayClosure, b); 
     } 
}); 

setSelectedDateは、物事の様々なを行いますが、それはまた、日付が選択されている意味する黄色のボタンの背景をオンにします。

私のエミュレータでは、すべてが期待通りに機能します。アクティビティが始まり、あなたは1日を押すと、その日は黄色に変わります。問題はありません。

しかし、私の仲間のエミュレータや物理的なデバイスのいくつかでは、何も起こりません... ListViewをスクロールするまで、そして突然選択した日が黄色に変わります。たとえば、「the 3rd」をタッチすると何も起こりません。数秒待ってからListViewをスクロールしてください(3番目ではないカレンダーの領域に触れる)、ListViewがスクロールするとすぐに3番目が魔法のように黄色に変わります。

この動作を示す私のピアエミュレータでは、onClickの第1行にブレークポイントを設定することができ、BPが実際にはListViewがスクロールされるまで、ヒットしないことがわかります。

この動作は私には意味がありません。私はonClickの振る舞いが、カプセル化されたViewのスクロールの努力と無関係であると期待します。

なぜこれが当てはまるのか、ボタンを押したときにonClicksがすぐに起きるように状況を修正する方法について考えてみましょうか?

ありがとうございました。

ポストScriptus:ArrayAdapterとListViewのコードは、要求された:

public class MonthArrayAdapter extends ArrayAdapter<Date> { 

    private MonthView[] _views; 

    private Vector<Procedure<Date>> _dateSelectionChangedListeners = new Vector<Procedure<Date>>(); 

    public MonthArrayAdapter(Context context, int textViewResourceId, Date minSelectableDay, Date maxSelectableDay) { 
     super(context, textViewResourceId); 
     int zeroBasedMonth = minSelectableDay.getMonth(); 
     int year = 1900 + minSelectableDay.getYear(); 

     if(minSelectableDay.after(maxSelectableDay)) 
     { 
      throw new IllegalArgumentException("Min day cannot be after max day."); 
     } 

     Date prevDay = minSelectableDay; 
     int numMonths = 1; 
     for(Date i = minSelectableDay; !sameDay(i, maxSelectableDay); i = i.addDays(1)) 
     { 
      if(i.getMonth() != prevDay.getMonth()) 
      { 
       numMonths++; 
      } 
      prevDay = i; 
     } 

     _views = new MonthView[numMonths]; 

     for(int i = 0; i<numMonths; i++) 
     { 
      Date monthDate = new Date(new GregorianCalendar(year, zeroBasedMonth, 1, 0, 0).getTimeInMillis()); 
      Date startSunday = findStartSunday(monthDate); 
      this.add(monthDate); 
      _views[i] = new MonthView(this.getContext(), startSunday, minSelectableDay, maxSelectableDay); 

      zeroBasedMonth++; 
      if(zeroBasedMonth == 12) 
      { 
       year++; 
       zeroBasedMonth = 0; 
      } 
     } 

     for(final MonthView a : _views) 
     { 
      a.addSelectedDateChangedListener(new Procedure<MonthView>() 
      { 
         @Override 
        public void execute(MonthView input) { 

         for(final MonthView b: _views) 
         { 
          if(a != b) 
          { 
           b.clearCurrentSelection(); 
          } 
         } 

         for(Procedure<Date> listener : _dateSelectionChangedListeners) 
         { 
          listener.execute(a.getSelectedDate()); 
         } 
        } 
      }); 
     } 




    } 

    void addSelectedDateChangedListener(Procedure<Date> listener) 
    { 
     _dateSelectionChangedListeners.add(listener); 
    } 

    private boolean sameDay(Date a, Date b) 
    { 
     return a.getYear() == b.getYear() && a.getMonth() == b.getMonth() && 
       a.getDate() == b.getDate(); 
    } 

    @Override 
    public View getView (int position, View convertView, ViewGroup parent) 
    { 
     return _views[position]; 
    } 


    private Date findStartSunday(Date d) 
    { 
     return d.subtractDays(d.getDay()); 
    } 

    public void setSelectedDate(Date date) 
    { 
     for(MonthView mv : _views) 
     { 
      mv.setSelectedDate(date); 
     } 
    } 



} 

public class DatePicker extends ActivityBase { 

    public static final String CHOSEN_DATE_RESULT_KEY = "resultKey"; 

    public static final String MIN_SELECTABLE_DAY = DatePicker.class.getName() + "MIN"; 

    public static final String MAX_SELECTABLE_DAY = DatePicker.class.getName() + "MAX"; 

    private static final String SELECTED_DATE = UUID.randomUUID().toString(); 

    private long _selectedDate = -1; 

    private MonthArrayAdapter _monthArrayAdapter; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Date now = new Date(); 

     Bundle inputs = this.getIntent().getExtras(); 

     long min = inputs.getLong(MIN_SELECTABLE_DAY, 0); 

     Date minSelectableDate; 
     if(min == 0) 
     { 
      minSelectableDate = new Date(now); 
     } 
     else 
     { 
      minSelectableDate = new Date(min); 
     } 
     Log.i(DatePicker.class.getName(), "min date = " + minSelectableDate.toString()); 
     long max = inputs.getLong(MAX_SELECTABLE_DAY, 0); 

     Date maxSelectableDate; 
     if(max == 0) 
     { 
      maxSelectableDate = new Date(now.addDays(35).getTime()); 
     } 
     else 
     { 
      maxSelectableDate = new Date(max); 
     } 


     setContentView(R.layout.date_picker); 

     Button doneButton = (Button) findViewById(R.id.DatePickerDoneButton); 

     if(doneButton == null) 
     { 
      Log.e(this.getClass().getName(), "Could not find doneButton from view id."); 
      finish(); 
      return; 
     } 

     doneButton.setOnClickListener(new OnClickListener() 
     { 
      @Override 
      public void onClick(View v) { 
       Intent result = new Intent(); 
       result.putExtra(CHOSEN_DATE_RESULT_KEY, _selectedDate); 
       setResult(RESULT_OK, result); 
       finish(); 
      } 
     }); 

     Button cancelButton = (Button) findViewById(R.id.DatePickerCancelButton); 

     if(cancelButton == null) 
     { 
      Log.e(this.getClass().getName(), "Could not find cancelButton from view id."); 
      finish(); 
      return; 
     } 

     cancelButton.setOnClickListener(new OnClickListener() 
     { 
      @Override 
      public void onClick(View v) { 
       setResult(RESULT_CANCELED, null); 
       finish(); 
      } 
     }); 

     ListView lv = (ListView)findViewById(R.id.DatePickerMonthListView); 
     lv.setDividerHeight(0); 


     _monthArrayAdapter = 
      new MonthArrayAdapter(this, android.R.layout.simple_list_item_1, minSelectableDate, maxSelectableDate); 

     _monthArrayAdapter.addSelectedDateChangedListener(new Procedure<Date>() 
       { 

        @Override 
        public void execute(Date input) { 
         _selectedDate = input.getTime(); 

        } 


       }); 

     lv.setAdapter(_monthArrayAdapter); 
    } 

    @Override 
    public void onRestoreInstanceState(Bundle savedInstanceState) 
    { 
     if(savedInstanceState.containsKey(SELECTED_DATE)) 
     { 
      _selectedDate = savedInstanceState.getLong(SELECTED_DATE); 
      _monthArrayAdapter.setSelectedDate(new Date(_selectedDate)); 
     } 
    } 

    @Override 
    public void onSaveInstanceState(Bundle savedInstanceState) 
    { 
     savedInstanceState.putLong(SELECTED_DATE, _selectedDate); 
    } 

} 
+0

は、あなたがしてください、あなたのアダプタのとあなたのListActivityの完全なコードを投稿することができますか? – nbarraille

+0

ここにアダプターがあります。 ListViewのために待機します。 – Dejas

+0

Ok、nbarraille、助けてくれることを願っています。 – Dejas

答えて

2

が答えを探して、同じ問題を持ちます。私は私のリストをスクロールするまで、私は私のonClickメソッドを取得しなかったとき、私は完全にそれを信じていませんでした。見つけたらここに回答を投稿します。

(スクロールスペースはクリックイベントに変わり、複雑なタッチイベントを食べているので)今のところ、私の最高の推測では、クリックのほか、さまざまなイベントを試してみることです。

「downViewは」追跡する静的変数であります要素がクリックされます。

view.setOnTouchListener(new View.OnTouchListener() { 
    @Override 
    public boolean onTouch(View v, MotionEvent event) { 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
     downView = v; 
     return true; 
    } else if (event.getAction() == MotionEvent.ACTION_UP) { 
     if (downView == v) { 
     handleClick(v); 
     return true; 
     } 
     downView = null; 
    } 
    return false; 
    } 
}); 
0

だから私は、手動でタッチイベントから独自のクリックイベントを構成するの、この外に完全に別の回答を追加します。

私は、Androidチームといくつかの電子メールを交換しました(グーグルが消費する特典がいくつかあります)。私はListAdapterを手作業で実装しようとすると効率が悪く、データオブザーバを正しく接続しないとアダプタのメソッドは、 "イベント処理に面白い問題を引き起こす可能性があります。「

だから私は次のようでした。

1)必要な機能をオーバーライドBaseAdapterのサブクラスでListAdapterの私の実装を置き換え

2)(list.invalidateViewsを使用して停止)とアダプタを使用して開始.notifyDataChanged()

とバグがなくなっているようだ。

手動でクリックイベントを構成するよりも多くの作業だが、それはまた、長期的にはより正しいコードです。

2

主な理由は、ListViewはビューの配列を持つアダプタが好きではないということです。リストビューのコードを見てみると

はそう問題は

public View getView (int position, View convertView, ViewGroup parent) 
{ 
    return _views[position]; 
} 

によってトリガーされる(またはむしろそれの両親AbsListView.obtainView方法)あなたはそれが起こることができるコード

if (scrapView != null) { 
     ... 
     child = mAdapter.getView(position, scrapView, this); 
     ... 
     if (child != scrapView) { 
      mRecycler.addScrapView(scrapView); 
のように表示されます getView(position,...)がscrapView!= _views [position]で呼び出されるため、scrapViewはリサイクルされます。一方、同じビューがListViewに再び追加され、ビューが奇妙な状態になる可能性があります( this issue参照)

最終的に、これはListView IMOで修正する必要がありますが、一時的に私はアドバイスしますビューの配列を含むアダプターを使用することに反対します。

0

Aswerは次のとおりです。

public View getView(int position, View convertView, ViewGroup parent) { 
    View v=makeMyView(position); 
    v.setFocusableInTouchMode(false); 
    return v; 
} 
関連する問題