クリック後64ミリ秒

アプリケーションがインターネットからデータをダウンロードし、ListViewに表示し、セルのクリックを処理した場合、読み続けることができます。 これは、リストセルをクリックした後、64ミリ秒間ペイントする方法についてのストーリーです。



クリックできないカテゴリとクリック可能なセルの2種類のセルがある通常のリストがありました



画像

サブカテゴリを持つランダムpiccha



使用したアダプタは次のとおりです。

github.com/siyusong/foodtruck-master-android/blob/master/src/com/foodtruckmaster/android/adapter/SeparatedListAdapter.java



データはサーバーからダウンロードされ、リストビューに表示されます。セルをクリックすると、詳細な説明を含む別の画面が開きます。

クリックを処理するために、AdapterView.OnItemClickListenerを使用しました。 getItemのアダプターは、詳細な説明の画面に渡されたオブジェクトを返しました。



クリック処理は次のように行われました。

public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Description desc = parent.getItemAtPosition(position); DescriptionActivity.open(context, desc); }
      
      







crashlyticsでは、ClassCastExceptionクラッシュ(文字列->説明)が表示され始めました。 これは、リスト内のクリック不可能な小見出しをクリックし、Descriptionオブジェクトの代わりにStringを得たことを意味します。 クリックできないセルは、performItemClickを使用してクリックできますが、そのようなメソッドは使用せず、リストと小見出しを含むすべての画面でクラッシュが発生しましたが、それらはほとんどありませんでした。



さらに、ソースコード4.2.2を掘り下げます。

AbsListView、onTouchEventメソッド

 case MotionEvent.ACTION_UP: { switch (mTouchMode) { case TOUCH_MODE_DOWN: case TOUCH_MODE_TAP: case TOUCH_MODE_DONE_WAITING: ... final AbsListView.PerformClick performClick = mPerformClick; ... if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) { ... if (mTouchModeReset != null) { removeCallbacks(mTouchModeReset); } mTouchModeReset = new Runnable() { @Override public void run() { mTouchMode = TOUCH_MODE_REST; child.setPressed(false); setPressed(false); if (!mDataChanged) { performClick.run(); } } }; if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { ... postDelayed(mTouchModeReset, ViewConfiguration.getPressedStateDuration()); } ... return true; } ... }
      
      







ビールなしでアンドロイドソースにアクセスしない方が良いでしょう。明らかに、OSの開発者は同じ原則に導かれたようです。

ここで、リストボックスをクリックして有効にすると、一定の間隔でPefrormClickを呼び出すことがわかります。 Android 4.2.2では、この間隔は64ミリ秒です。



Runnable PerformClickのように見えます

 private class PerformClick extends WindowRunnnable implements Runnable { int mClickMotionPosition; public void run() { // The data has changed since we posted this action in the event queue, // bail out before bad things happen if (mDataChanged) return; final ListAdapter adapter = mAdapter; final int motionPosition = mClickMotionPosition; if (adapter != null && mItemCount > 0 && motionPosition != INVALID_POSITION && motionPosition < adapter.getCount() && sameWindow()) { final View view = getChildAt(motionPosition - mFirstPosition); // If there is no view, something bad happened (the view scrolled off the // screen, etc.) and we should cancel the click if (view != null) { performItemClick(view, motionPosition, adapter.getItemId(motionPosition)); } } } }
      
      







この実行可能オブジェクトは、OnItemClickListenerが既に呼び出されているperformItemClickを呼び出します。 アダプター内のデータが変更された場合、それが注がれることがわかります。 アダプタの境界などを確認してください。 最も興味深いのは、新しいアダプターをインストールし、古いアダプターのデータを変更しない場合、mDataChangedがfalseになることです。isEnabledセルのチェックがないことに注意する価値があります。



つまり セルをクリックし、64ミリ秒以内にアダプターを変更すると、このランナブルが実行され、その結果、クリックは電話で見たデータではなく、新しいものに従って発生します。 また、新しいアダプターでセルがisEnabled = falseの場合、引き続きクリックされると、onItemClickListenerが呼び出されます。



次の行で:

 Description desc = parent.getItemAtPosition(position);
      
      





奇跡的にClassCastExceptionが発生しました



解決策:これは明らかにバグであり、最も簡単な解決策はmDataChangedフラグをtrueに設定するか、アダプターを変更するときにメッセージキューをクリアすることです。



結論:

セルをクリックし、その時点でサーバーから新しいデータがダウンロードされ、リストにインストールされた場合、新しいデータをクリックしました(アダプターを再度作成した場合)。

複数のセルタイプとアイテムオブジェクトがある場合は、nullおよびinstanceofでgetItemAtPositionメソッドの結果を常に確認してください。



All Articles