LinearLayoutやRecyclerViewの、もともと用意されている区切り線について

LinearLayout

android:divider="?android:dividerHorizontal"
android:showDividers="beginning|middle|end"

RecyclerView

final DividerItemDecoration decoration = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(decoration);

もともと用意されているDrawable

android:background="?android:attr/listDivider"

Androidで、v7.Toolbarに高さ指定するときについて

ActionBarをMaterial対応させると、Heightは
56dp (デフォルト)
48dp (横向き)
64dp (タブレット、sw600dp)
になると思うが、実際に直指定するのはださい。 ICSからNougatまでに対応し、なおかつ直指定しない方法として、

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/original" />

とすれば良さそう。

?android:attr/actionBarSize

ではなく

?attr/actionBarSize

と指定する。つまり、Compatの値を呼ぶようにする。

コードによる、actionBarSize の確認方法

// これは56dp
final TypedArray styledAttributes = getContext().getTheme().obtainStyledAttributes(
      new int[]{R.attr.actionBarSize});
final int px = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
final int dp = px / (int) getResources().getDisplayMetrics().density;

Log.d("size", String.valueOf(dp));
// これだと48dpになる
final TypedArray styledAttributes = getContext().getTheme().obtainStyledAttributes(
      new int[]{android.R.attr.actionBarSize});
final int px = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
final int dp = px / (int) getResources().getDisplayMetrics().density;

Log.d("size", String.valueOf(dp));

appcompat-v7 を使って、カスタマイズ可能な、 Dialog のような画面を超楽にめちゃ綺麗につくる

Androidで、カスタマイズ可能なDialogのような画面を楽につくる方法について考える。

Android でカスタマイズダイアログをつくるのは、AlertDialogよりはDialogFragmentのほうが楽にはなっているが、相変わらず骨が折れる。

今回は、DialogはAlertDialogやDialogFragmentで作るもの、という発想は捨てて、ActivityにMaterial Themeに用意されているDialogのStyleを適応することで、簡単に綺麗に実装していく方法で進めるというアプローチを取る。

ライブラリ的に用意するものは、Googleから提供されている、Support Libraryのうちの一つ、 appcompat-v7のみ。今Android Studioで新規プロジェクトを作ると、予めappのbuild.gradleに記載されているので、導入に特別な手順も必要ない。

後は、AppCompatActivityを用意し、Manifest側でDialogのThemeを適応するだけ。
Dialogの起動も、Activityと同じくstartActivityを呼び出すだけですし、回転やタスク切り替えの特別な配慮も必要なくなる。なんと素晴らしい!

GitHubにサンプルを置く。局所的にコードを書き記すよりも全体を見たほうが状況を把握しやすいはずである。このサンプルは、Dialogの中身がWebViewとなっていて、画面を立ち上げるとGoogleのトップページが開く。Activityをベースにつくると、こういった奇抜な画面も作成可能(笑)。

f:id:tatuas:20170508152520p:plain

https://github.com/tatuas/AndroidWebViewDialogSample

Deploygateから、サンプルコードをビルドしたAPKを配布する。 Try it on your device via DeployGate

上記サンプルで使用している Theme は、中身の大きさが Style 側で dp 指定されている、 Theme.Base.AppCompat.Dialog.FixedSize という Theme だが、要件によっては、そのサイズをカスタマイズしたい需要もあるかもしれない。ただ、ちょっと Style の命名が直感的でないので、以下の Stackoverflow を参考にされると良い。

http://stackoverflow.com/questions/29003591/android-fixed-size-dialog-dimension-what-is-major-minor

上記を参考に、
values
values-large
values-xlarge
にそれぞれdimens.xmlでサイズを定義して、style.xmlでその値に Override すればよい。サンプルコードには、サイズを変更する実装も含む。

FragmentLifecycleCallbacksってのがあるらしいが…

https://developer.android.com/reference/android/support/v4/app/FragmentManager.FragmentLifecycleCallbacks.html

使えそうだなーと思い。

が、ドキュメントではstaticでメンバークラスぽいけど、実際のコードではインナークラスだった…

なのでこんな感じで書けばいけました。

final FragmentManager.FragmentLifecycleCallbacks cb = getSupportFragmentManager().new FragmentLifecycleCallbacks() {
        @Override
        public void onFragmentStarted(FragmentManager fm, Fragment f) {
            super.onFragmentStarted(fm, f);
            Log.d("log", "onFragmentStarted");
        }
};

getSupportFragmentManager().registerFragmentLifecycleCallbacks(cb, true);

FirebaseCrashクラスにあるlogとlogcatメソッドの違い

# 致命的クラッシュや、FirebaseCrash.report()した場合に送られる際に、直近のログを同時に送信するため保存しておく。保存する内容はlogcatには出力しない。
FirebaseCrash.log(message);

# 致命的クラッシュや、FirebaseCrash.report()した場合に送られる際に、直近のログを同時に送信するため保存しておく。保存する内容はlogcatにも出力する。
FirebaseCrash.logcat(Log.ERROR, tag, message);

メッセージ自体は、レポートの直近に出力されたログとしてFirebase Consoleで見られるだけで、BigQueryには自動的にエクスポートはされない模様。

macOS Sierraで、Homebrew経由でMySQLをインストール&使用開始できる状態にする

Macにインストール

$ brew install mysql

MySQLの初期設定

$ mysqld --initialize --explicit_defaults_for_timestamp

で初期設定。

--initialize specified but the data directory has files in it. Aborting

というエラーメッセージが出た場合は、${Homebrewのインストールディレクトリ}/var/mysqlディレクトリが存在しているから。存在していれば削除して初期設定をやり直す。 成功したら

A temporary password is generated for root@localhost: XXXXXXXX

などと仮のrootパスワードが出てくるので忘れずにメモ。

my.cnfでsockファイルの場所を指定

$ mysql --help | grep my.cnf

でmy.cnfの場所を検索。

                      order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf ${Homebrewのインストールディレクトリ}/etc/my.cnf ~/.my.cnf 

などと出てくるはず。好みの場所にmy.cnfファイルを作成。 中身は

[client]
socket=/tmp/mysql/mysql.sock
[mysqld]
socket=/tmp/mysql/mysql.sock

とする。

Sockファイル作成

$ cd /tmp
$ sudo mkdir mysql
$ cd mysql
$ sudo touch mysql.sock

権限設定

${Homebrewのインストールディレクトリ}/var/mysql

/tmp/mysql

${好みの場所に書いたmy.cnfのディレクトリ}/my.cnf

/tmp/mysql/mysql.sock

4つのディレクトリ・ファイルの権限を整える。

$ cd /tmp/mysql
$ sudo chown -R ${Macのユーザ名。whoamiで出るもの。}:wheel mysql/
$ cd ${Homebrewのインストールディレクトリ}/var
$ sudo chown -R ${Macのユーザ名。whoamiで出るもの。}:wheel mysql/
$ cd ${好みの場所に書いたmy.cnfのディレクトリ}
$ sudo chown ${Macのユーザ名。whoamiで出るもの。}:wheel my.cnf
$ cd /tmp/mysql
$ sudo chown ${Macのユーザ名。whoamiで出るもの。}:wheel mysql.sock

MySQLのサーバを起動

$ mysql.server start

とやって、

Sucess!

と出れば成功。

MySQLのクライアントを起動

$ mysql -u root -p

で上記でメモした仮パスワードを入れてログイン。 ログインできたら成功。 ログイン後は、仮パスワードを変更しないと何も操作できないので、

mysql> set password for root@localhost=password('パスワード');

で、rootのパスワードを更新する。

セキュア設定

$ mysql_secure_installation

を実行するだけ。

Androidで表示されているViewをファイル保存する

View view = findViewById(R.id.preview);
Bitmap bitmap = null;
OutputStream stream = null;
boolean result = false;
try {
    final String filename = "saved.jpg";
    final File saved = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename);
    final int quality = 100;
    stream = new FileOutputStream(saved);

    // 通常はCacheを使用したほうが処理は早い。
    final boolean isAutoScale = false;
    view.buildDrawingCache(isAutoScale);
    bitmap = view.getDrawingCache(isAutoScale);
    result = bitmap.compress(Bitmap.CompressFormat.JPEG, quality, stream);
    view.destroyDrawingCache();

    // 上のキャッシュを利用するやり方だと
    // ”View too large to fit into drawing cache, needs ~ bytes, only ~ available”
    // と出ることがある。其の場合は下記のやり方で。
    // ただし端末によっては処理が遅い。
    bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);

    Toast.makeText(getContext(), (result && saved.exists()) ? "Succeed." : "Failure.", Toast.LENGTH_SHORT).show();
} catch (Throwable throwable) {
    throwable.printStackTrace();
} finally {
    try {
        if (bitmap != null) {
            bitmap.recycle();
        }

        if (stream != null) {
            stream.close();
        }
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
}

Ubuntu の Unity ランチャーに任意のアプリケーションを登録する

$ cd /usr/local/applications
$ vim 任意.desktop

以下は内容

Desktop Entry]
Version=1.0
Type=Application
Name=任意
Exec="コマンドの場所" %f
Icon=アイコン画像の場所
Categories=Development;IDE;
Terminal=false
StartupNotify=true
StartupWMClass=任意
Name[en_GB]=任意.desktop

再ログイン後反映されているはず。

Robolectric 3.0でApplicationクラスやConstantsクラスをrobolectric.propertiesに書き出す時の注意点

src/test/resources/robolectric.properties(正しい例)

application=com.sample.MyApplication
constants=com.sample.BuildConfig
sdk=21
packageName=com.sample

src/test/java/com/sample/test/MyTest.java(端折っています)

import com.sample.MyApplication;

@Config(application = MyApplication.class)

流れ的に@Config(application = MyApplication.class)の要領でapplication=com.sample.MyApplication.classと書くと

java.lang.RuntimeException: Could not load class: com.sample.MyApplication.class

となると思う。

が、.classはpropertiesに書く際は必要ない。

クラスパスはフルパスで記載するので注意。

Google App Engine for Pythonでdev_appserver.pyした際エラーが出たら

ImportError: cannot import name docker

などと出るだろう。 以下の方法で対処可能。

$ pip install docker-py
$ vim ~/google-cloud-sdk/platform/google_appengine/google/appengine/tools/docker/containers.py

- from docker import docker
+ import docker

:wq