Почему мое масштабируемое векторное масштабирование не соответствует ожидаемому?

Я пытаюсь использовать векторные рисунки в своем приложении для Android. С http://developer.android.com/training/material/drawables.html (выделено мной):

В Android 5.0 (уровень API 21) и выше вы можете определять векторные элементы рисования, которые масштабируются без потери определения.

Используя этот drawable:

<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 точек на дюйм (на большом мобильном устройстве высокого разрешения около 2015 года с леденцом на палочке):

blurryBellIcon

Изменение ширины и высоты в определении вектора, отрисовываемого до 200dp, значительно улучшает ситуацию при отображаемом размере 400dp. Однако, установив его в качестве отрисовки для элемента TextView (т.е. значка слева от текста), теперь создается огромный значок.

Мои вопросы:

1) Почему указывается ширина / высота в векторе? Я думал, что весь смысл в том, что они масштабируются вверх и вниз без потерь, делая ширину и высоту бессмысленными в своем определении?

2) Можно ли использовать один векторную графику, которая работает как текст на 24 дп в TextView, но хорошо масштабируется и использовать как можно больше изображений? Например, как мне избежать создания нескольких векторных изображений разных размеров и вместо этого использовать один, который масштабируется до моих отрисованных требований?

3) Как эффективно использовать атрибуты width/height и в чем разница с viewportWidth/Height?

Дополнительные детали:

  • Устройство работает с API 22
  • Использование Android Studio v1.5.1 с Gradle версии 1.5.0
  • Манифест - это компиляция и цель 23, минимальный уровень 15. Я также пытался переместить минимальный уровень на 21, но это не имело значения.
  • Декомпиляция APK (с минимальным уровнем, установленным в 21) показывает один ресурс XML в папке drawable. Растеризованные изображения не создаются.

10 ответов

Решение

Новая информация об этой проблеме здесь:

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

Похоже на использование android:scaleType="fitXY" заставит его правильно масштабироваться на Lollipop.

От инженера Google:

Привет, дайте мне знать, если scaleType='fitXY' может быть для вас обходным путем, чтобы изображение выглядело четким.

Зефир против Lollipop из-за специальной обработки масштабирования, добавленной в зефир.

Также, для ваших комментариев: "Правильное поведение: векторное изображение должно масштабироваться без потери качества. Поэтому, если мы хотим использовать один и тот же актив в 3 разных размерах в нашем приложении, нам не нужно дублировать vector_drawable.xml 3 раза с разными жестко закодированные размеры. "

Несмотря на то, что я полностью согласен с этим, в действительности платформа Android имеет проблемы с производительностью, так что мы еще не достигли идеального мира. Поэтому на самом деле рекомендуется использовать 3 разных вектора vector_drawable.xml для повышения производительности, если вы уверены, что хотите нарисовать 3 разных размера на экране одновременно.

Техническая деталь в основном заключается в том, что мы используем растровое изображение в ловушке для кэширования рендеринга сложного пути, так что мы можем получить лучшую производительность перерисовки наравне с перерисовкой растрового изображения.

1) Почему указывается ширина / высота в векторе? Я думал, что весь смысл в том, что они масштабируются вверх и вниз без потерь, делая ширину и высоту бессмысленными в своем определении?

Для версий SDK менее 21, где система сборки должна генерировать растровые изображения и в качестве размера по умолчанию в тех случаях, когда вы не указываете ширину / высоту.

2) Можно ли использовать один векторный объект рисования, который работает как текст на 24 дп в TextView, а также большое изображение шириной около экрана?

Я не верю, что это возможно, если вам нужно настроить таргетинг на SDK менее 21.

3) Как эффективно использовать атрибуты width/height и в чем разница с 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 или @drawable в коде XML; при запуске приложения соответствующее векторное или растровое изображение отображается автоматически в зависимости от уровня API.


Редактировать: происходит что-то странное. Вот мои результаты в эмуляторе SDK версии 23 (тестовое устройство Lollipop+ сейчас мертво...):

Хорошие колокола на 6.х

И в эмуляторе SDK версии 21:

Дрянные колокольчики на 5.х

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();
        }
    }
}

Наконец, убедитесь, что вы используете app:srcCompat вместо android:src установить изображение вашего ImageView:

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

Ваши векторные рисунки теперь должны корректно масштабироваться!

1) Почему указывается ширина / высота в векторе? Я думал, что весь смысл в том, что они масштабируются вверх и вниз без потерь, делая ширину и высоту бессмысленными в своем определении?

Это просто размер вектора по умолчанию, если вы не определяете его в макете. (т.е. вы используете обтекание для высоты и ширины вашего изображения)

2) Можно ли использовать один векторный объект рисования, который работает как текст на 24 дп в TextView, а также большое изображение шириной около экрана?

Да, это возможно, и у меня не было проблем с изменением размера, если на работающем устройстве используется леденец или выше. В предыдущих API при преобразовании вектора он преобразовывался в png для разных размеров экрана.

3) Как эффективно использовать атрибуты width/height и в чем разница с viewportWidth/Height?

Это влияет на то, как используется пространство вокруг вашего вектора. т. е. вы можете использовать его, чтобы изменить "гравитацию" вектора внутри области просмотра, сделать вектор с полем по умолчанию, оставить некоторые части вектора вне области просмотра и т. д. Обычно вы просто устанавливаете их на то же самое размер.

Я решаю свою проблему, просто изменив размер векторного изображения. С самого начала это был ресурс вроде:

<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">

Это работает для меня.

Я пробую эти способы, но, к сожалению, ни один из них не помог. я попробую fitXY в ImageViewЯ пытаюсь использовать app:srcCompat но даже не могу его использовать. но я нашел способ хитрости:

установите Drawable в background вашей ImageView,

Но суть этого способа заключается в измерениях видов. если твой ImageView имеет неправильные размеры, растягиваемый получает Stretch. Вы должны контролировать его в версиях перед леденцом на палочке или использовать некоторые виды PercentRelativeLayout,

Надеюсь, это полезно.

Для меня причина, по которой векторы были преобразованы в png, была android:fillType="evenodd" атрибут в моем векторе drawable. Когда я удалил все вхождения этого атрибута в моем рисовании (таким образом, вектор, используя по умолчанию nonzero Тип заполнения), в следующий раз приложение было все хорошо.

Первый: импортировать svg в drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html))

1 Щелкните правой кнопкой мыши на папке для рисования и выберите New -> Vector Asset

введите описание изображения здесь

2- Vector Asset Studio предоставляет вам возможность выбрать встроенные значки материалов или ваш собственный локальный файл SVG. При необходимости вы можете переопределить размер по умолчанию.

введите описание изображения здесь

Второе: если вы хотите получить поддержку от Android API 7+, вы должны использовать поддержку библиотеки Android (( /questions/32605211/ne-najden-identifikator-resursa-dlya-atributa-v-pakete-comapp/32605244#32605244))

1- добавить

vectorDrawables.useSupportLibrary = true

в ваш файл build.gradle.

2-Используйте пространство имен в вашем макете, который имеет imageView:

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

Третье: установите imageView с android:scaleType="fitXY" и используйте app:srcCompat

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

В-четвертых: если вы хотите установить svg в java-коде, вы должны добавить строку ниже при создании метода

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

решено добавлением

      defaultConfig {
    vectorDrawables.useSupportLibrary = true
}

в файл buil.gradle (module:app) и для imageViews заменить

      android:src="..."

с участием

      app:srcCompat="..."

надеюсь помочь

Помимо основных ответов, на моей стороне переход к ConstraintLayout вместо LinearLayout или FrameLayout удаляет размытие на векторном SVG-чертеже.

Другие вопросы по тегам