365連休

にわかのandroidとかの開発メモ。

【解決】Android 4.4以下でDrawable Resource(ic_vector.xml)を使用するレイアウトがあると、実行時にandroid.content.res.Resources$Not-Found-Exceptionが発生する

前の記事の続きです。

適当に設定を書き換えているだけでは埒が明かないので、一から歴史を勉強してきました。

 

そもそもVector Drawableリソースを使えるようになったのはAPI21 Android5.0 Lollipopからで、
それよりも前のバージョンでDrawableリソースを扱うために、Support Library23.2からで対応された。Support Library23.2以上を利用することでAPI19 Android4.4KitKat~API11 Android3.0 HoneycombでDrawableリソースを扱うことができるようになった。ということらしいです。

developer.android.com

android-developers.googleblog.com

 

Android Studio 3.4で新規にプロジェクトを作った場合、特に意識することなく適用されています。たぶん。

↓build.gradle

// Gradle Plugin 2.0+
android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
  compile 'com.android.support:appcompat-v7:23.2.0' //最低バージョン
}

 

f:id:neet_rookie:20190514220735p:plain

公式 スクショ引用

実際にVector Drawableリソースを使うときにはSupport Library向けにコードを書く必要があります。

ImageView及びそのサブクラスでVector Drawableリソースを使用する場合、android:src属性ではなくapp:srcCompat属性で指定しなければいけません。

<ImageView  
  android:layout_width="wrap_content"  
  android:layout_height="wrap_content"  
  app:srcCompat="@drawable/ic_add" />

 

 ImageView以外でVector Drawableリソースを使いたい場合…これについては、公式情報を見つけることができませんでした。

が、幸いQiitaにて素晴らしい記事を発見。

qiita.com

TextViewの場合は、直接VectorDrawableを指定すると落ちます。 そのため、StateListDrawableを作成してその中でVectorDrawableを指定してあげる必要があります。

 

なるほど、ということでVector Drawableに対応するState List Drawableを作成します。

State List Drawableについてはよく知りませんが、Stateに応じてDrawableを変えるセレクタらしいです。

 

f:id:neet_rookie:20190514205749p:plain

上がVector Drawableリソース、下がそれに対応するState List Drawableリソース

 

で、ActivityのLayoutファイル内でDrawableリソースを使用する時には@drawable/ic_hoge_stateを指定してやります。

 

ImageView以外ではうまく動作しないSupport LibraryのVector Drawableの後方互換機能が、stateファイルがあるとうまく動作するようになるのかなと・・・想像です。

 

Android Developers Blog Support Library23.2の記事の中ほどになぜか取り消されている部分を読んでみると

However, AppCompat does support loading vector drawables when they are referenced in another drawable container such as a StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable, and RotateDrawable. By using this indirection, you can use vector drawables in cases such as TextView’s android:drawableLeft attribute, which wouldn’t normally be able to support vector drawables.

StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable, and Rotate Drawableを使うとTextViewのandroid:drawableLeft属性でもVector Drawableがサポートされる…みたいなことが書いてある?

 

 

ただ、私の環境ではまだ設定が足りませんでした。

 

stackoverflow.com

 

AppCompat + Vector Resources cause large memory usage [37090849] - Visible to Public - Issue Tracker "https://issuetracker.google.com/issues/37090849"

 

medium.com

qiita.com

 

 

Top ActivityまたはApplicationにてstaticでAppCompatDelegate.setCompatVectorFromResourcesEnabled(true);を実行します。

public class MainActivity extends AppCompatActivity {
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    ...
}

Support Library 23.4から使える設定みたいです。

意味はよく分かりません。

medium.comの記事"Android: Crash when using vector Drawables on Pre Lollipop."によると

For AppCompat users, we’ve added an opt-in API to re-enable support Vector Drawables from resources (the behavior found in 23.2) via AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) - keep in mind that this still can cause issues with memory usage and problems updating Configuration instances, hence why it is disabled by default.

 (23.3で) 23.2のバグを修正するオプトインを追加したけど、メモリ使用量に関する問題を持っているので(23.4では)デフォルトで無効になっている…みたいなことが書いてある?

 

上記すべての設定を行うことで無事SeekBarにVector Drawableリソースを適用することができました。

  • build.gradleでvectorDrawables.useSupportLibrary = true
  • compile 'com.android.support:appcompat-v7:23.4.0' //以上を使う
  • 全てのVector Drawableに対してState List Drawableを作成する。
  • AppCompatActvtyにstaticでAppCompatDelegate.setCompatVectorFromResourcesEnabled(true);を追加する

 

ただし、メモリ使用量の問題があるとの事なので、minSDKがAPI19 Android4.4 KitKat 以下なら素直にPNGを使う方が無難というのが、個人的な結論です。
Stateファイル作るのも面倒ですし。

 

以上

 

丸一日つぶれたぁぁぁぁあああ(# ゚Д゚)