ベクタードローアブルスケーリングが期待どおりにならないのはなぜですか?


97

Androidアプリでベクタードローアブルを使用しようとしています。http://developer.android.com/training/material/drawables.htmlから(鉱山を強調):

Android 5.0(APIレベル21)以上では、定義を失うことなくスケーリングできるベクタードローアブルを定義できます

このドロアブルを使用する:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/colorPrimary" android:pathData="M14,20A2,2 0 0,1 12,22A2,2 0 0,1 10,20H14M12,2A1,1 0 0,1 13,3V4.08C15.84,4.56 18,7.03 18,10V16L21,19H3L6,16V10C6,7.03 8.16,4.56 11,4.08V3A1,1 0 0,1 12,2Z" />

そしてこのImageView:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    android:src="@drawable/icon_bell"/>

アイコンを400 dpで表示しようとすると、次のぼやけた画像が生成されます(lollipopを実行している2015年頃の高解像度モバイルデバイス上):

blurryBellIcon

ベクタードローアブルの定義で幅と高さを200dpに変更すると、400dpのレンダリングサイズでの状況が大幅に改善されます。ただし、これをTextView要素のドローアブルとして設定すると(つまり、テキストの左側にあるアイコン)、巨大なアイコンが作成されます。

私の質問:

1)ベクタードローアブルに幅/高さの仕様があるのはなぜですか?これらの全体のポイントは、ロスレスにスケールアップおよびスケールダウンして、その定義で幅と高さを無意味にすることだと思いましたか?

2)TextViewで24 dpのドローアブルとして機能する単一のベクタードローアブルを使用することはできますか。たとえば、さまざまなサイズの複数のベクタードローアブルを作成せずに、レンダリングされた要件に合わせてスケーリングする方法を使用するにはどうすればよいですか?

3)幅/高さの属性を効果的に使用するにはどうすればよいですか?viewportWidth / Heightとの違いは何ですか?

さらなる詳細:

  • デバイスはAPI 22を実行しています
  • Android Studio v1.5.1とGradleバージョン1.5.0の併用
  • マニフェストはコンパイルおよびターゲットレベル23、最小レベル15です。また、最小レベルを21に移動しようとしましたが、これには違いがありませんでした。
  • APKを逆コンパイルすると(最小レベルは21に設定されます)、ドローアブルフォルダーに単一のXMLリソースが表示されます。ラスタライズされた画像は生成されません。

ただ明確にします。Androidスタジオを使用していますか?Android StudioのどのバージョンとGradleプラグインのどのバージョンですか?Android Studioでドローアブルフォルダーを右クリックして選択するNew -> Vector Assetと、ベクターイメージXMLがドローアブルフォルダーにドロップされます。ただし、apktoolを使用してビルドされたAPKを解凍すると、XMLファイルが存在しdrawable-anydpi-v21、API 21以降のデバイスで正しくスケーリングされていることがわかります。ラスターファイルはdrawable-<mdpi/hdpi/etc>-v4フォルダーに配置され、API 21以降のデバイスでは使用されません(正しくスケーリングされるという事実に基づく)
Cory Charlton

@Cory Charlton。奇妙な。Studio 1.5.1とGradle 1.5.0を使用しています。最小レベルを21に変更した後、画像がAPKに表示される唯一の場所はdrawableフォルダーです。ベクター型ドローアブルxmlファイルの幅/高さは24 dpです。400dpの高さ/幅のImageViewを指定すると、確実にスケーリングが不十分な画像が作成されます。
クリスナイト

それが私が期待することです。drawable-anydpi-v21minSdkVersionが21未満になるのは、私がで終わる唯一の理由です。21未満に設定した場合の動作の変更はありますminSdkVersionか?XMLの移動についてはどうdrawable-anydpiですか?変更があるとは思っていませんが、ベクター画像が正しくスケーリングされていると思います...
Cory Charlton

最小バージョンを15に戻すと、同じ結果になります。drawable-anydpi-v21mdi / hdpi / etc内のさまざまなラスター化された画像を含む単一のxmlファイル。フォルダ。ただし、レンダリング結果の最終的な変更はありません。
クリスナイト

非常に奇妙です...私はあなたの画像を使用して私のアプリの1つを変更し、それは正常に動作します(私の編集された回答を参照してください)
Cory Charlton

回答:


100

この問題に関する新しい情報がここにあります:

https://code.google.com/p/android/issues/detail?id=202019

を使用 android:scaleType="fitXY"すると、Lollipopで正しくスケーリングされるようです。

Googleエンジニアから:

こんにちは、画像を鮮明に見せるために、scaleType = 'fitXY'が回避策になるかどうかをお知らせください。

マシュマロ対ロリポップは、マシュマロに追加された特別なスケーリング処理によるものです。

また、コメントについて:「正しい動作:ベクタードローアブルは品質を損なうことなくスケーリングする必要があります。したがって、アプリケーションで3つの異なるサイズで同じアセットを使用する場合は、vector_drawable.xmlを3回異なるサイズで複製する必要はありません。ハードコードされたサイズ。」

これは事実だと私は完全に同意していますが、実際には、Androidプラットフォームにはパフォーマンスの懸念があるため、まだ理想的な世界に到達していません。したがって、画面上に同時に3つの異なるサイズを描画したい場合は、パフォーマンスを向上させるために、実際には3つの異なるvector_drawable.xmlを使用することをお勧めします。

技術的な詳細は、基本的にビットマップをフックの下で使用して複雑なパスのレンダリングをキャッシュし、ビットマップドローアブルの再描画と同等の最高の再描画パフォーマンスを得ることができるようにすることです。


27
OK Google、同じサイズの異なるビューポートとパスを持つ同一のドローアブルをコピーアンドペーストする必要があるのは絶対に恐ろしいことです。
Miha_x64

これにより、ロゴがピクセル化して表示されていたデバイスで問題が修正されましたが、以前はすべて問題がなかった他のユニットでは、アスペクト比が間違っています。これが問題である理由がわかりません。それはベクトルです。ピクセルではありません!:P
tplive 2017年

@Harish Gyanani、android:scaleType = "fitXY"は問題を解決しますが、四角いアイコンではなく動的なアイコンはどうですか?
マヌエラ、

28

1)ベクタードローアブルに幅/高さの仕様があるのはなぜですか?これらの全体のポイントは、ロスレスにスケールアップおよびスケールダウンして、その定義で幅と高さを無意味にすることだと思いましたか?

ビルドシステムがラスターイメージを生成する必要がある21未満のSDKバージョンの場合、および幅/高さを指定しない場合のデフォルトサイズ。

2)TextViewで24 dpのドローアブルとして機能する単一のベクトルドローアブルと、画面に近い幅の大きな画像を使用することはできますか?

21未満のSDKもターゲットにする必要がある場合、これが可能であるとは思いません。

3)幅/高さの属性を効果的に使用するにはどうすればよいですか?viewportWidth / Heightとの違いは何ですか?

ドキュメンテーション:(私がもう一度読んだので、実際にはあまり役に立ちません...)

android:width

ドローアブルの固有の幅を定義するために使用されます。これは、通常dpで指定されるすべての次元単位をサポートします。

android:height

ドローアブルの固有の高さを定義するために使用されます。これは、通常dpで指定されるすべての次元単位をサポートします。

android:viewportWidth

ビューポートスペースの幅を定義するために使用されます。ビューポートは基本的に、パスが描画される仮想キャンバスです。

android:viewportHeight

ビューポートスペースの高さを定義するために使用されます。ビューポートは基本的に、パスが描画される仮想キャンバスです。

その他のドキュメント:

Android 4.4(APIレベル20)以下はベクタードローアブルをサポートしていません。最小APIレベルがこれらのAPIレベルのいずれかに設定されている場合、Vector Asset Studioは、下位互換性のためにベクタードローアブルのラスターイメージを生成するようにGradleに指示します。ベクトルDrawableコードはJavaコードまたは@drawableXMLコードで参照できます。アプリを実行すると、APIレベルに応じて、対応するベクターまたはラスター画像が自動的に表示されます。


編集:変なことが起こっています。エミュレータSDKバージョン23での私の結果を次に示します(Lollipop +テストデバイスは現在停止しています...):

6.xの良い鐘

エミュレータSDKバージョン21の場合:

5.xの不機嫌そうな鐘


1
Coryに感謝します。そのドキュメントを見たことがありますが、あまり役に立ちませんでした。私のデバイスはLollipop(v22)ですが、Height / WeightがImageViewで正確に指定されているかどうかに関係なく、ベクタードローアブルで指定された24 dpを超えてグラフィックを適切にスケーリングできません。ターゲットとコンパイルレベル23と最小レベル21でマニフェストをテストしましたが、ドローアブル自体の幅/高さを変更しないと、ベクタードローアブルをスムーズに拡大できません。高さ/幅だけが異なる複数のドローアブルを作成したくありません!
クリスナイト

1
確認ありがとうございます。本当にありがとうございました。あなたが示したように、それは私が期待するように明らかに機能するはずです。正確なコードを使用しても、最初と同じ結果が得られます。イライラ!
クリスナイト

1
更新:API 22のエミュレーターに対してコードを実行しましたが、同じ結果が得られます。API 23を使用してエミュレーターに対してコードを実行しました。アップスケーリングはAPI 23以上のデバイスでのみ機能しているようです。ターゲットSDKのバージョンを変更しようとしましたが、違いはありませんでした。
クリスナイト

この問題の解決策は見つかりましたか?
dazza5000

@corycharltonそれは削除しても大丈夫ですwidth height viewport heightwidthからxml
VINNUSAURUS

9

AppCompat 23.2では、Android 2.1以降を実行するすべてのデバイスにベクタードローアブルが導入されています。ベクタードローアブルのXMLファイルで指定された幅/高さに関係なく、画像は正しくスケーリングされます。残念ながら、この実装はAPIレベル21以降では使用されていません(ネイティブ実装のため)。

良いニュースは、AppCompatがAPIレベル21および22でその実装を使用するように強制できることです。悪いニュースは、これを行うためにリフレクションを使用する必要があることです。

まず、AppCompatの最新バージョンを使用していることと、新しいベクターの実装を有効にしていることを確認してください。

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.2.0'
}

次に、useCompatVectorIfNeeded();アプリケーションのonCreate()を呼び出します。

private void useCompatVectorIfNeeded() {
    int sdkInt = Build.VERSION.SDK_INT;
    if (sdkInt == 21 || sdkInt == 22) { //vector drawables scale correctly in API level 23
        try {
            AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
            Class<?> inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate");
            Class<?> vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate");

            Constructor<?> constructor = vdcInflateDelegateClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            Object vdcInflateDelegate = constructor.newInstance();

            Class<?> args[] = {String.class, inflateDelegateClass};
            Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args);
            addDelegate.setAccessible(true);
            addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

最後に、ImageViewの画像を設定するapp:srcCompat代わりにandroid:srcを使用していることを確認してください:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    app:srcCompat="@drawable/icon_bell"/>

これでベクタードローアブルが正しくスケーリングされます。


良いアプローチですが、AppCompat 25.4(およびおそらく以前のバージョン)では、「同じライブラリグループからのみ呼び出すことができます」というlintエラーが発生します。私が使用しているビルドツールを使用してもビルドは可能ですが、ランタイムに影響があるかどうかはわかりません。テストしようとしています。
androidguy 2017年

同じライブラリグループからのみ呼び出すことができます。これを追加してこのエラーを削除します:lintOptions {disable 'RestrictedApi'}
Usama Saeed US

6

1)ベクタードローアブルに幅/高さの仕様があるのはなぜですか?これらの全体のポイントは、ロスレスにスケールアップおよびスケールダウンして、その定義で幅と高さを無意味にすることだと思いましたか?

これは、レイアウトビューでベクターを定義しない場合のベクターのデフォルトサイズです。(つまり、画像ビューの高さと幅にラップコンテンツを使用します)

2)TextViewで24 dpのドローアブルとして機能する単一のベクトルドローアブルと、画面に近い幅の大きな画像を使用することはできますか?

はい、可能です。実行中のデバイスがロリポップ以上を使用している限り、サイズ変更に問題はありません。以前のAPIでは、アプリをビルドすると、ベクターはさまざまな画面サイズのpngに変換されました。

3)幅/高さの属性を効果的に使用するにはどうすればよいですか?viewportWidth / Heightとの違いは何ですか?

これは、ベクターの周囲のスペースの使用方法に影響します。つまり、これを使用して、ビューポート内のベクトルの「重力」を変更したり、ベクトルにデフォルトのマージンを設定したり、ベクトルの特定の部分をビューポートから除外したりすることができます。通常、それらを同じに設定しますサイズ。


エミウラありがとう。同じドローアブルを使用して複数のレンダリングサイズを実現する方法に興味があります。24 dpのドローアブルを使用して(テスト目的で)、ImageViewを400 dpの指定された幅と高さを持つように変更し、Lollipopデバイス(v22)で実行しましたが、ベクターイメージではなく、ラスターアップスケールイメージのようにレンダリングが不十分です。また、マニフェストを変更して、ターゲットを設定し、v23および最小バージョン21に対してコンパイルしました。違いはありません。
クリスナイト

この場合、ベクタードローアブルで最大のサイズを使用し、テキストビューで24 dpのサイズを指定するだけで済みます。
emirua

ベクトルドローアブルのサイズとLollipopのレイアウトのサイズの非常に大きな違いで拡大すると、ぼやけた画像が表示されるようになりました。ただし、画面サイズごとにファイルの束を用意するのではなく、最大サイズを設定するだけで、はるかに優れています。
emirua

4

ベクター画像のサイズを変更するだけで問題が解決します。最初からそれは次のようなリソースでした:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="300"
    android:viewportWidth="300">

そして、私はそれを次のように150dp(このベクターリソースを使用したレイアウトのImageViewのサイズ)に変更しました。

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="150dp"
    android:height="150dp"
    android:viewportHeight="300"
    android:viewportWidth="300">

それは私のために働いています。


おかげで、これは私のタブレットでピクセル表示された問題を解決するには十分です。
user2342558

3

私はこれらの方法を試してみる必要がありますが、残念ながら、どれもうまくいきませんでした。試してみるfitXYImageView使ってみたapp:srcCompatけど使えない。しかし私はトリックの方法を見つけました:

のDrawableをbackgroundに設定しますImageView

しかし、この方法の要点はビューのディメンションです。あなたがあればImageView、誤った寸法を有し、描画可能なストレッチを取得します。あなたはロリポップ前のバージョンでそれを制御するか、いくつかのビューを使用する必要がありますPercentRelativeLayout

お役に立てば幸いです。


2

最初に svgをdrawbleにインポートします:((https://www.learn2crack.com/2016/02/android-studio-svg.html))

ドローアブルフォルダーを1右クリックし、[新規]-> [ベクターアセット]を選択します。

ここに画像の説明を入力してください

2- Vector Asset Studioには、組み込みのマテリアルアイコンまたは独自のローカルsvgファイルを選択するオプションがあります。必要に応じて、デフォルトのサイズを上書きできます。

ここに画像の説明を入力してください

2つ目:Android API 7以降からのサポートが必要な場合は、Android Support Libraryのコードを使用する必要があります((https://stackoverflow.com/a/44713221/1140304))

1-追加

vectorDrawables.useSupportLibrary = true

あなたのbuild.gradleファイルに。

2- imageViewを持つレイアウトで名前空間を使用します。

xmlns:app="http://schemas.android.com/apk/res-auto"

3番目:android:scaleType = "fitXY"でimageViewを設定し、app:srcCompatを使用します

 <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        app:srcCompat="@drawable/ic_menu" />

4番目:Javaコードでsvgを設定したい場合は、oncreate methoedの行の下に追加する必要があります

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

ソリューションはAPI 21以上で動作しますか?上記のuser3210008は、実装がAPI 21以降では使用されないことを述べています。
AJW 2018

2

私にとって、ベクターがpngに変換されるandroid:fillType="evenodd"原因は、ベクタードローアブルの属性でした。ドローアブル内のこの属性の出現をすべて削除したとき(したがって、デフォルトのnonzero塗りつぶしタイプがベクターに適用されたため)、次にアプリをビルドしたときはすべて問題ありませんでした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.