AppCompat 25.1.0 では効かない Android 4.4.x の backgroundTint

最初の私のイメージとしては,

サポートライブラリが充実してきているので ボタンの色は, banckgroundTint で簡単に変更できる!

でしたが...

おさらい

まず, ボタンを設置します.


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:text="Button" />

灰色のボタンが表示されて押したら, それなりにエフェクトが効いて「押されました感」が見て分かります.

ボタンの色を変えます.


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:text="Button"
        android:background="@color/colorAccent" />

ボタンの色が変わりましたが, 押したときにエフェクトが効きません.

background 属性のかわりに backgroudTint を使います.


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:text="Button"
        android:backgroundTint="@color/colorAccent" />

これで, 背景色も変わりかつエフェクトも効きます.

background ではなく, backgroundTint で色を指定する

ということですね.

Button と AppCompatButton の記述

StackOverflow などを見ているとさまざまな記述が見えます.

android - Lollipop's backgroundTint has no effect on a Button - Stack Overflow

ただ「ボタンの色を変えたいだけ」なのですが, なんだか混乱しています.

backgroudTint を使った記述にも似たようなものいくつかあるようです.


    <android.support.v7.widget.AppCompatButton
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:text="AppCompatButton"
        android:backgroundTint="@color/colorAccent" />


    <android.support.v7.widget.AppCompatButton
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:text="AppCompatButton"
        app:backgroundTint="@color/colorAccent" />

Button のかわりに AppCompatButton を使っていたり, backgroundTint の prefix が android: となっていたり app: になっています.

とりあえず, すべてをレイアウトに記述して表示してみました.

以下3つの挙動は予想通りで同じです.


<Button
    android:backgroundTint="@color/colorAccent"
    ...


<AppCompatButton
    android:backgroundTint="@color/colorAccent"
    ...


<AppCompatButton
    app:backgroundTint="@color/colorAccent"
    ...

Button と記述した場合でも, 内部でいい感じに入れ替えてくれているようですね.

[Tool]-[Android]-[Layout Inspector]

AppCompat を意識すること無く, 昔からの Button を使って記述していけばサポートライブラリが対応してくれるということですね!

AppCompat は えらい!!

Android 4.4 KITKAT で確認する

結果 : 全てダメ.

え,

色とエフェクト両方きちんと反映されているものがない...

なんなんすかね これ...

AppCompat のバージョンを下げてみます.

//compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:appcompat-v7:25.0.1'

Android 4.4 では, backgroudTint が ひとつの記述だけしか意図通りに表示してくれません.

AppCompat はクソ!!

そら混乱もしますわ.

まとめ

backgroundTint についての
OSバージョンと appcompat-v7 バージョンの関係

現状では,

AppCompat-v7 を 25.0.1 で


   <android.support.v7.widget.AppCompatButton
	....
        app:backgroundTint="@color/colorAccent" />

と書いておくのが吉.


JSR-310 で Date/Calendar を避けて通る

Android上で既存のフォーマットを利用して日時を表示しようとすると, android.text.format.DateUtils は利用したい.

「ThreeTenABP」で捨てれないのか android.text.format.DateUtils

JSR-310系の LocalDate などと Unixタイム間を自在に変換できない.

LocalDateTime系は時差情報を持たないので、時刻の起点が定まっていない(システム依存)という特徴があります。一方でInstantとjava.util.Dateは暗黙的にUTCという時差情報をもっており、時刻の起点が定まっています。これがLocalDateTime.toInstantなるメソッドがない理由です

Java8日付時刻APIの使いづらさと凄さ - きしだのはてな

「Local* は時刻の起点をもっていない」

ので, それを追加した ZonedDateTime/OffsetDateTime に一度変換して Instant を経由して Epoc へ, 逆も同じ要領で.


long epochSecondF = 1234567890;
long epochMilliF = epochSecondF * 1000L;

Instant instantF = Instant.ofEpochMilli(epochMilliF);
//Instant instantF = Instant.ofEpochSecond(epochSecond);

ZonedDateTime zonedDateTime = instantF.atZone(ZoneOffset.systemDefault());
//OffsetDateTime offsetDateTime = instantF
//    .atOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.EPOCH));

zonedDateTime = zonedDateTime
    .plusDays(123)
    .plusHours(123)
    .plusMinutes(123)
    .plusMonths(123)
    .plusWeeks(123)
    .minusDays(123)
    .with(TemporalAdjusters.lastDayOfMonth())
    .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));

//offsetDateTime = offsetDateTime
//    .plusDays(123);
//    .plusHours(123)
//    .plusMinutes(123)
//    .plusMonths(123)
//    .plusWeeks(123)
//    .minusDays(123)
//    .with(TemporalAdjusters.lastDayOfMonth())
//    .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));

Instant instantR = zonedDateTime.toInstant();
//Instant instantR = offsetDateTime.toInstant();

long epochMilliR = instantR.toEpochMilli();
//long epochSecondR = instantR.getEpochSecond();

のように, タイムゾーンやオフセットの情報は, Android端末の設定側から取得する.


ZoneOffset.systemDefault()


ZoneOffset.systemDefault().getRules().getOffset(Instant.EPOCH)

利用者の属するタイムゾーンを利用してカレンダー周りの計算をして, Epoch と行き来するなら,


  public static ZonedDateTime toZonedDateTime(long epochMilli) {
    return Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault());
  }

  public static long toEpocMilli(ZonedDateTime zonedDateTime) {
    return zonedDateTime.toInstant().toEpochMilli();
  }

そして表示の前にフォーマットする.


String formatted = DateUtils.formatDateTime(context, toEpocMilli(zonedDateTime), 
       FORMAT_SHOW_YEAR | FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_ABBREV_ALL);

// 2016年12月14日(水) 10:13
// Wed, 14 Dec 2016 00:13
// Tue, Dec 13, 2016, 20:13
// 2016年12月14日週三 09:13
// 2016년 12월 14일 (수) 10:13
// ...

「ThreeTenABP」で捨てれないのか android.text.format.DateUtils


【本当に32ビット?】Android端末の CPU の ABI を特定する

なんとなく選択していた OpenGApps の「Platform」.

The Open GApps Project

この「ARM」とか「x86」ての結局何なのか.

調べてみました.

概要

「ABI (Application Binary Interface)」とは, Android のプロセッサーにセットされた命令の種類で, mylib.so のようなネイティブCライブラリをアプリに組み込むときにABIを特定してコンパイルする必要がある.

app/libs/armeabi/mylib.so
app/libs/x86/mylib.so

大きく3つのカテゴリに分けられる.


ARM
	- armeabi
	- armeabi-v7a
	- arm64-v8a
X86
	- x86
	- x86_64
MIPS
	- mips
	- mips64

それぞれのカテゴリ内は下のもののほうが新しく一般的に下位互換なので, arm64-v8 では armeabi-v7a 向けのアプリを使うことができる.

armeabi-v7a が多いが最近は arm64-v8a が増えてきている雰囲気.

Arm Arm64 x86 Selection Guide for Android -iTechify

Genymotionなどのエミュレータ や ASUS Zenphone 2 は x86 らしい.

端末のABIを特定する

コマンドラインから眺めてみたりしてましたが,

shell@mako:/ $ cat /proc/cpuinfo
Processor : ARMv7 Processor rev 2 (v7l)
processor : 0
BogoMIPS : 13.53
...

こっちの方が良さげ.

shell@mako:/ $ getprop | grep cpu
[ro.product.cpu.abi]: [armeabi-v7a]
[ro.product.cpu.abi2]: [armeabi]
[ro.product.cpu.abilist]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist32]: [armeabi-v7a,armeabi]
[ro.product.cpu.abilist64]: []

How to identify the android device is 32-bit or 64-bit ? | Intel® Software

コード内で取得するにはそれぞれ以下で.

Build.CPU_ABI | Android Developers

Build.CPU_ABI2 | Android Developers

Build.SUPPORTED_ABIS | Android Developers

まとめ

私のAndroidには「ARM」を選択しますわ!