2013/03/18

onListItemClick() の中で isChecked() を呼んではいけない?

少し前に、知人に頼まれて作った Androidアプリの動作がおかしいとのこと。
どうも、Galaxy Nexus を 4.0.2 → 4.1.1 にアップデートしてからおかしくなったらしい。

調べてみると、ListActivity.onListItemClick() の中で CheckedTextView.isChecked() を呼んだ時の挙動が変化した模様。

というわけで、以下のようなコードを用意して実験してみました。
public class MainActivity extends ListActivity {
    private static final String TAG = "MainActivity";

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

        String[] listItems = { "1", "2", "3" };

        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        ListAdapter adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_multiple_choice, listItems);
        setListAdapter(adapter);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        // ↓ 少なくとも Android 4.2 までは空関数
        // super.onListItemClick(l, v, position, id);
        CheckedTextView cv = (CheckedTextView) v;

        Log.d(TAG, "isChecked " + cv.isChecked());
    }
}

これを、Android 4.0.4 のエミュレータで動作させると以下の通り
// OFF → ON
isChecked false

// ON → OFF
isChecked true

Android 4.2 のエミュレータで動作させると以下の通り
// OFF → ON
isChecked true

// ON → OFF
isChecked false

完全に動作が逆転しています…

そもそも、以前は「チェックしたのに isChecked が false」という直感に反した仕様だったので、正しい動作をするようになったと言えるのかもしれません。

ただ、この変更がどのような意図で行われたのかが不明です。
Android Developersはチェックしたのですが、それらしい記述を見つけることはできませんでした。
バグフィックスなのか、たまたま挙動が変わってしまったのか…
もし、詳しい方がいらっしゃいましたら、コメントやTwitterで是非教えてください。

とりあえずは、以下のように Android のバージョンをチェックして乗り切ることは可能かもしれません。
        CheckedTextView cv = (CheckedTextView) v;

        boolean isChecked = (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) ?
                !cv.isChecked() : cv.isChecked();

ただし、仕様が明確に提示されていない以上、今後もまた変更が入る可能性はあります。
つまり、onListItemClick() の中では isChecked() を呼ばない方が無難だという事になります。

しかし、チェックされた瞬間に動作させたいことってありますよねぇ…

続き: OnItemClickListener.onItemClick() の中で isChecked() を呼んではいけない?

0 件のコメント: