読者です 読者をやめる 読者になる 読者になる

最近ハマった事象の備忘録(SwitchCompat)

android support-library SwitchCompat

はじめに

新年あけましておめでとうございます

去年は、年末に歯痛になったり*1スマホ専用手袋*2の片方紛失したり

とかあんまりいい感じじゃありませんでしたが

今年はいいこと有るといいなーと思いつつ、今年も頑張りたいと思います

ハマリポイント

SwitchCompatに関して、色々とハマリポイントが有るのでメモしておく

単純に使うだけなら問題ないのですが

  • ListView
  • RecyclerView

等で ViewHolder 等でSwitchCompatを 使いまわすと挙動がおかしくなります


導入

appcompatに同梱されている

  • build.gradle
dependencies {
    compile 'com.android.support:appcompat-v7:24.1.+'
}

カスタマイズ記事


ListViewで罫線が消えることがある

Lolipopの一部の端末で発生

対処法

<style name="Color1SwitchStyle">
    <item name="colorControlActivated">@color/my_awesome_color</item>
</style>
<RelativeLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    ...>

    <android.support.v7.widget.SwitchCompat
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:theme="@style/Color1SwitchStyle"/>

プログラムで書くなら

ContextThemeWrapper ctw = ContextThemeWrapper(this, R.style.Color1SwitchStyle); 
SwitchCompat sw = new SwitchCompat(ctw)

初期化時/再表示時に勝手に ON/OFF が動いてしまう

  • 初期表示時
  • notifyDataSetChangedで再表示時

のときの現象。(Array)Adapter::sort 処理とかも絡めるとさらにカオスw

対処法

stackoverflow だと下記なコードがでてきますが、前者より後者のほうがベターかと

  • 微妙なコード (一度削除して追加し直し)
ViewGroup viewGroup = (ViewGroup) view; // the recycled view
viewGroup.removeView(switch);
switch.setChecked(states[index]);
viewGroup.addView(switch);
  • betterなコード(OnTouchListenerで弾く方法)
private Boolean isTouched = false;

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    switchButton.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                isTouched = true;
                return false;
            }
        });

    switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
    {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
        {
            if (isTouched) {
                isTouched = false;
                if (isChecked) {
                }
                else {
                }
            }
        }
    });

}

setOnCheckedChangeListenerをOnTouchされない限り動かさないようにするコードです

RecycleViewだと特に onBindViewHolder でClickListner等をつけることを強いられるのでこの方法がベーターかと

SwitchCompat を連打すると残像が残る

SwitchCompatにデフォルトに設定されているアニメーションが動いて残像に見えてしまう現象

style.xml指定で殺せればいいんですけどね。。

対処法

    @Override
    public void setChecked(boolean checked) {
        super.setChecked(checked);

        // Calling the super method may result in setChecked() getting called
        // recursively with a different value, so load the REAL value...
        checked = isChecked();

        if (getWindowToken() != null && ViewCompat.isLaidOut(this)) {
            animateThumbToCheckedState(checked); //■デフォルトのアニメーション
        } else { //★
            // Immediately move the thumb to the new position.
            cancelPositionAnimator();
            setThumbPosition(checked ? 1 : 0);
        }
    }

と実装されていて、WindowToken が nullのときにアニメーションを無効にできるようなので

  • SwitchCompatを継承したクラスを作成
  • getWindowToken() の戻り値に強制的に null を返すように改変

したものをlayout.xmlに設定すれば一応対処はできる。

ただこれだと完全にアニメーションが消えてしまうんですよね・・

Nexus 7でつまみ部分が表示されなかった(以前の体験)

一応下記のissueで対応はされているらしい

ただ、現在 Nexus 7サイズ のタブレットって実は殆どリリースされていなかったりするので

最新版がちゃんと動くか確認しづらいかもな。。と

*1:歯医者に緊急医療はないので年内治療が受け付けてもらえず、痛み止めずっと飲んでてオセチまともにっ食べれなかったり。。

*2:ちょっと高かったorz