DataBindingでSpannableStringを使いたい

TextViewの装飾でSpannableStringを使っているのに、ObservableField()を使って値を設定すると装飾が消える。

ObservableFiled()を使って設定すれば装飾が残るのでこっちを使おう。

RealmにEnumを保存したい

RealmはEnumを保存できない。なので、nameを保存してそれを使ってenumが引ける口を作ると簡単だと思う。

WorkerManager#beginUniqueWork で複数登録する場合はDelayを入れたほうが良い

WorkerManagerのbeginUniqueWorkでWorkerを大量に突っ込むと3つ目で以下のエラーを吐いて落ちる

E/WorkerWrapper: Status for aa9f2b0d-d327-4a9f-bac1-5f3e6799603a is ENQUEUED; not doing any work

以下のようにちょっとだけDelayをいれると動いた。

うまいことWorkerをまとめる術があるのなら他にやったほうが良さそうだが、beginUniqueWorkでまとめる場合はDelayをいれたほうが無難だと思われる。

Google Sign-InでApiException: 12501がでて失敗する

以下のエラーがでて失敗する

> com.google.android.gms.common.api.ApiException 12501

手順は以下の通り

OAuth2のClientKeyが設定されてないのがだめというものを見つけたけど違った。

結局、 GoogleSignInOptions.DEFAULT_SIGN_IN を設定しないといけないところを GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN を設定していたからエラーとなっていた。

うまくいったコード

バージョンは以下

> implementation “com.google.android.gms:play-services-auth:15.0.1”

FirabseのAuthUIで[Code: 10, message: 10]のエラーが出る

FirebaseのAuthUIを試してみたけど以下のエラーがでて動かない。

com.firebase.ui.auth.FirebaseUiException: Code: 10, message: 10:

よくよくみると以下のワーニングが出ていた

Developer error: this application is misconfigured. Check your SHA1 and package name in the Firebase console.

SH256のほうがいいだろうと256しか登録していなかったが、SHA1が登録していないとだめらしい。FirebaseConsoleの設定からSHA1を登録したところ問題なく動作した。

複数のFlowableで全ての更新通知で全ての値を結合する

結論を先に書くと Flowable.combineLatest を使うと良い

flatMapで結合

こうすると、onNextが流れるたびにobserveされる物が増えていくためintProcessorで流れてきた回数strProcessorが出力されることになる。一応目的は果たして入るが、同じ値が複数流れてくるので非常に効率が悪い。

combineLatestで結合

こうすると、どちらかのonNextが流れてきたときに、他の最新データを使って結合することができる。これがやりたかったこと。

TextViewのMarqueeが動かない問題に終止符を

TextViewのMarqueeが動かない問題でいつも悩んでいるので色々なところを参考にして以下のクラスを作った。これならどの場面でも動くことは動く。

以下が参考になる。

http://saways.blogspot.jp/2011/10/textview-marquee.html
 saways.blogspot.jp 

どちらも最終的な結論は「isFocuse()でtrueを返せ!」なんだけど、isFocuse()でtrueを返すとフォーカスがTextViewにあることになるのでキーボードが開けないという困ったことが起こる。なので、StackTraceからMarqueeに必要な関数から呼ばれているかを判定してtrueを返答するようにしてるんだろうけどこれだと関数名が変わったり、別の箇所から呼ばれたりとRefactorされただけで動かなくなってしまいそうで怖い。

実際にTextViewの中身を見ると && (isFocuse() || isSelected()) で判定をしているので、どちらかを返答すればよいのだからisSelected()でtrueを返答してあげたほうが実害が少ないのでこっちを採用することで僕の環境だと上手く動いた。XMLで色々やったりしてもどうしても動かない箇所が出てくるのでこれで動かすのが今のところのベスト。もし違う実装が見つかったらまた書くかも。

rxAndroidのsubscribeOnとobserveOnの違い

よくわからなかったので動かしながら試したけど結論は

スレッドを変えたかったらobserveOnを使え!

ってことだった。

お試し1

お試し2

解説

ログを取って確認してみると以下のように動いている。

subscribeOn
Observableの動き出しのスレッドを指定。最初に指定されたものが使われる。
observeOn
以降が動くスレッドを指定:

となっている。

僕の理解ではObservable.just()の部分もmainで動くのかと思ったけど、Observable.create()からsubscribeOnで指定されたスレッドで動くっぽいように見える。subscribeOnはLibraryとか配るときにObservable.create()を動かすスレッドを固定することが出来るっていうのが利点。ライブラリ作って配布するとかしないのであれば使う必要なくてスレッドを変えたいだけならobserveOnを使わなければならない。

(続)ListViewのEmptyViewが使えないので代替案を考える

これの続き

ViewTypeで分けてみる

前に作った方法でやるとどうしても無理矢理っぽい。なので、ViewTypeにEmptyを追加して0件なら表示するように変更してみた。

public class ListAdapter extends ArrayAdapter {

    // 表示するViewType達
    private enum ViewType {
        NORMAL(0),
        EMPTY(1);

        private int key;

        ViewType(int key) {
            this.key = key;
        }

        public int getKey() {
            return key;
        }

        public static ViewType getByKey(int key) {
            ViewType ret = null;
            for (ViewType viewType : values()) {
                if (key == viewType.getKey()) {
                    ret = viewType;
                    break;
                }
            }
            return ret;
        }
    }

    @Override
    public boolean isEmpty() {
        return (super.getCount() == 0 && mEmptyView == null) ? true : super.isEmpty();
    }

    @Override
    public int getCount() {
        // 0件かつEmptyViewが設定されていたらEmptyView分の1を返答する
        return (super.getCount() == 0 && mEmptyView != null) ? 1 : super.getCount();
    }

    @Override
    public int getViewTypeCount() {
        return ViewType.values().length;
    }

    @Override
    public int getItemViewType(int position) {
        // 0件かつEmptyViewが設定されていたらEmptyViewになる
        return (position == 0 && super.getCount() == 0 && mEmptyView != null) ?
                ViewType.EMPTY.getKey() : ViewType.NORMAL.getKey();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;

        switch (ViewType.getByKey(getItemViewType(position))) {
            case NORMAL:
                view = getNormalView(position, convertView, parent);
                break;
            default:
                // defaultになることはない。
            case EMPTY:
                view = getEmptyView(position, convertView, parent);
                break;
        }

        return view;
    }

    private View getEmptyView(int position, View convertView, ViewGroup parent) {
        // ここでEmptyViewを作成する
        return view;
    }

    private View getNormalView(int position, View convertView, ViewGroup parent) {
        // ここで通常のViewを作成する
        return view;
    }
}

まとめ

前回よりもこっちのほうがいいと思う。convertViewで再利用もちゃんとできるしいい感じ。setEmptyViewとかも合わせてOverrideした方がいいかも。

Toolbarでmarqueeを実現する

追記

こっちのほうが上手く動く


昔のWebサイトでよく見た文字が流れるやつはandroidでも実現できる。TextViewにellisize=marqueeを指定すれば良いんだけど、設定しただけじゃ動いてくれなくてfocusbleとかいろいろいれないといけない。

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:scrollHorizontally="true"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:singleLine="true"/>

それで、ツールバーでも同じように設定すればいいかと思わせてそれじゃ動かない。なので、toolbarにmakrkerを設定したTextViewを入れてそこにタイトルを設定するっていう力技しか無いっぽい。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <TextView
            android:id="@+id/toolbar_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:scrollHorizontally="true"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:singleLine="true"/>

</android.support.v7.widget.Toolbar>

コードからTitleを設定する

        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        mToolbarTitle = (TextView) findViewById(R.id.toolbar_title);

        setSupportActionBar(mToolbar);
        mToolbarTitle.setText(getSupportActionBar().getTitle());
        getSupportActionBar().setTitle("");
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

すごい力技。もうちょっときれいな方法はなかろうか。とりあえず動くは動く。ただ、ListViewとかだとこれでも動かない場合がある。その場合はフォーカスを与えると動く

        mToolbarTitle.setSelected(true);
        mToolbarTitle.requestFocus();