2020年3月10日火曜日

Material Design Components for Android 1.1.0 でボタンのデフォルトカラーが colorAccent から colorPrimary に変わった

Theme.AppCompat.Light.DarkActionBar



Theme.MaterialComponents.Light.DarkActionBar (1.0.0)



Theme.MaterialComponents.Light.DarkActionBar (1.1.0)





何もしてないのに(MDC の version を 1.1.0 に上げたけど...) 色が!変わった!

ピンクはどこの色かというと colorAccent に指定している色です。では緑はどこの色かというと colorPrimary に指定している色です。

ボタン系のデフォルトカラーが 1.1.0 から colorPrimary に変わったようです。



AlertDialog のボタンの色は

?attr/materialAlertDialogTheme に指定されている
ThemeOverlay.MaterialComponents.MaterialAlertDialog

Base.ThemeOverlay.MaterialComponents.MaterialAlertDialog

Base.V14.ThemeOverlay.MaterialComponents.MaterialAlertDialog の <item name="buttonBarPositiveButtonStyle">@style/Widget.MaterialComponents.Button.TextButton.Dialog</item> <item name="buttonBarNegativeButtonStyle">@style/Widget.MaterialComponents.Button.TextButton.Dialog</item> <item name="buttonBarNeutralButtonStyle">@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush</item>
Widget.MaterialComponents.Button.TextButton.Dialog の <item name="android:textColor">@color/mtrl_text_btn_text_color_selector</item>
@color/mtrl_text_btn_text_color_selector <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:alpha="1.00" android:color="?attr/colorPrimary" .../> <item android:alpha="0.60" android:color="?attr/colorOnSurface" .../> <item android:alpha="1.00" android:color="?attr/colorPrimary" .../> <item android:alpha="0.38" android:color="?attr/colorOnSurface"/> </selector> あー、colorPrimary と colorOnSurface になったのねぇ。

ちなみに 1.0.0 のときの @color/mtrl_text_btn_text_color_selector では colorAccent 使ってます。 <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="?attr/colorAccent" android:state_enabled="true"/> <item android:color="@color/mtrl_btn_text_color_disabled"/> </selector>


ボタンの色を変えたいときは 「AlertDialog の Negative ボタンの文字色を変える」 と同じ感じでやればOK <style name="ThemeOverlay.MyApp.MaterialAlertDialog" parent="ThemeOverlay.MaterialComponents.MaterialAlertDialog"> <item name="buttonBarPositiveButtonStyle">@style/Widget.MyApp.Button.TextButton.Dialog</item> <item name="buttonBarNegativeButtonStyle">@style/Widget.MyApp.Button.TextButton.Dialog</item> </style> <style name="Widget.MyApp.Button.TextButton.Dialog" parent="Widget.MaterialComponents.Button.TextButton.Dialog"> <item name="android:textColor">#1565C0</item> </style> AlertDialog.Builder(this, R.style.ThemeOverlay_MyApp_MaterialAlertDialog) .setTitle("Title") .setMessage("Message") .setPositiveButton(android.R.string.ok, null) .setNegativeButton(android.R.string.cancel, null) .show()



2020年3月9日月曜日

Material Design Components for Android 1.1.0 から Checkbox で android:button を指定するなら app:useMaterialThemeColors="false" も必要(なことが多い)

Checkbox のマークをカスタマイズするときは android:button に drawable resource を指定します。

例えば以下のような drawable を用意して Checkbox の android:button に指定したのが次のスクリーンショットの下2つです。 <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:bottom="7dp" android:left="7dp" android:right="7dp" android:top="7dp"> <selector> <item android:state_checked="true"> <shape android:shape="oval"> <size android:width="18dp" android:height="18dp" /> <stroke android:width="6dp" android:color="#6666ff" /> </shape> </item> <item> <shape android:shape="oval"> <size android:width="18dp" android:height="18dp" /> <stroke android:width="2dp" android:color="#cccccc" /> </shape> </item> </selector> </item> </layer-list> <CheckBox ... android:button="@drawable/checkbox" ... />



全く同じコードで Activity の theme を Material Design Components for Android 1.0.0 に変えたのが次のスクリーンショットです。



同じようになってますね。

全く同じコードで Activity の theme を Material Design Components for Android 1.1.0 に変えたのが次のスクリーンショットです。



なんということでしょう!android:button で指定した drawable resource が Material Design の theme color で tint されるようになりました。tint で使われる色は colorControlActivated と colorOnSurface です。

この挙動を止めるには app:useMaterialThemeColors="false" を指定します。 <CheckBox ... android:button="@drawable/checkbox" app:useMaterialThemeColors="false" ... />



tint されなくなりました!



2020年3月6日金曜日

Facebook sdk で <meta-data> を設定しているのに初期化されていないと怒られる問題に対応した

いやー、まったくひどい落とし穴でしたよ。

もとの構成はこんな感じです(facebookAppId の値はダミーです)。

build.gradle android { ... defaultConfig { ... manifestPlaceholders = [facebookAppId: "1234567890000000"] } ... } dependencies { ... implementation "com.facebook.android:facebook-android-sdk:6.1.0" implementation "com.facebook.android:facebook-share:6.1.0" } <?xml version="1.0" encoding="utf-8"?> <manifest ...> ... <application ...> ... <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="${facebookAppId}" /> <provider android:name="com.facebook.FacebookContentProvider" android:authorities="com.facebook.app.FacebookContentProvider${facebookAppId}" android:exported="true" /> </application> </manifest>
公式のドキュメント(https://developers.facebook.com/docs/android/getting-started#app_id)には meta-data を AndroidManifest に書けば勝手に初期化処理がされると書いてあるのに、実際に動かすと以下の初期化処理が走っていないというエラーで落ちます。

java.lang.ExceptionInInitializerError
     Caused by: The SDK has not been initialized, make sure to call FacebookSdk.sdkInitialize() first.


調べてみると FacebookSdk の loadDefaultsFromMetadata() の中で meta-data から読みんだ com.facebook.sdk.ApplicationId の値が Float になっていることが判明しました!(facebookAppId が全て数字だからそうなるのでしょうか...)

下記の Object appId が Float になってしまっていたのです。そのためその後の if 文に入らず、applicationId がセットされていませんでした。

FacebookSdk.java // Package private for testing only static void loadDefaultsFromMetadata(Context context) { if (context == null) { return; } ApplicationInfo ai = null; try { ai = context.getPackageManager().getApplicationInfo( context.getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { return; } if (ai == null || ai.metaData == null) { return; } if (applicationId == null) { Object appId = ai.metaData.get(APPLICATION_ID_PROPERTY); if (appId instanceof String) { String appIdString = (String) appId; if (appIdString.toLowerCase(Locale.ROOT).startsWith("fb")) { applicationId = appIdString.substring(2); } else { applicationId = appIdString; } } else if (appId instanceof Integer) { throw new FacebookException( "App Ids cannot be directly placed in the manifest." + "They must be prefixed by 'fb' or be placed in the string resource file."); } } ... } ここの処理を読むと、appId の最初に fb or FB がついているときはそれを省いた部分を applicationId にしていることがわかります。

そこで以下のように meta-data の value を "fb${facebookAppId}" にしたら怒られなくなりました! <?xml version="1.0" encoding="utf-8"?> <manifest ...> ... <application ...> ... <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="fb${facebookAppId}" /> <provider android:name="com.facebook.FacebookContentProvider" android:authorities="com.facebook.app.FacebookContentProvider${facebookAppId}" android:exported="true" /> </application> </manifest>


ちなみに facebookAppId を string resource で用意した場合 <string name="facebook_app_id">1234567890000000</string> とか android { ... defaultConfig { ... resValue "string", "facebook_app_id", "1234567890000000" } ... } のときは、fb を付けなくても com.facebook.sdk.ApplicationId の値は String として読み込まれます。 <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id" />