[android] 벡터 드로어 블 크기 조정이 예상대로되지 않는 이유는 무엇입니까?

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

아이콘을 400dp로 표시하려고 할 때이 흐릿한 이미지를 생성합니다 (롤리팝을 실행하는 2015 년 경의 거대한 고해상도 모바일 장치에서).

blurryBellIcon

벡터 드로어 블 정의에서 너비와 높이를 200dp로 변경하면 400dp 렌더링 크기에서 상황이 크게 개선됩니다. 그러나이를 TextView 요소 (예 : 텍스트 왼쪽의 아이콘)에 대한 드로어 블로 설정하면 이제 거대한 아이콘이 생성됩니다.

내 질문 :

1) 벡터 드로어 블에 너비 / 높이 사양이있는 이유는 무엇입니까? 나는 이것들의 전체 요점은 그들이 정의에서 너비와 높이를 무의미하게 만드는 무손실로 확장 및 축소된다는 것이라고 생각했습니다.

2) TextView에서 24dp 드로어 블로 작동하지만 훨씬 더 큰 이미지를 사용하기 위해 잘 확장되는 단일 벡터 드로어 블을 사용할 수 있습니까? 예를 들어 크기가 다른 여러 벡터 드로어 블을 만드는 것을 피하고 대신 렌더링 된 요구 사항에 맞게 조정되는 것을 사용하는 방법은 무엇입니까?

3) 너비 / 높이 속성을 효과적으로 사용하는 방법과 viewportWidth / Height의 차이점은 무엇입니까?

추가 세부 사항:

  • 장치가 API 22를 실행 중입니다.
  • Gradle 버전 1.5.0과 함께 Android Studio v1.5.1 사용
  • 매니페스트는 컴파일 및 목표 레벨 23, 최소 레벨 15입니다. 또한 최소 레벨을 21로 이동하려고 시도했지만 차이가 없습니다.
  • APK를 디 컴파일하면 (최소 레벨이 21로 설정 됨) 드로어 블 폴더에 단일 XML 리소스가 표시됩니다. 래스터 화 된 이미지는 생성되지 않습니다.


답변

여기에이 문제에 대한 새로운 정보가 있습니다.

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을 사용하는 것이 좋습니다.

기술적 세부 사항은 기본적으로 복잡한 경로 렌더링을 캐시하기 위해 후크 아래에 비트 맵을 사용하여 비트 맵 드로어 블을 다시 그리는 것과 동등한 수준으로 최상의 다시 그리기 성능을 얻을 수 있다는 것입니다.


답변

1) 벡터 드로어 블에 너비 / 높이 사양이있는 이유는 무엇입니까? 나는 이것들의 전체 요점은 그들이 정의에서 너비와 높이를 무의미하게 만드는 무손실로 확장 및 축소된다는 것이라고 생각했습니다.

빌드 시스템이 래스터 이미지를 생성해야하는 21 미만의 SDK 버전의 경우 너비 / 높이를 지정하지 않은 경우 기본 크기로 사용합니다.

2) TextView에서 24dp 드로어 블과 큰 화면 너비 이미지로 작동하는 단일 벡터 드로어 블을 사용할 수 있습니까?

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에 지시합니다. DrawableJava 코드 또는 @drawableXML 코드에서 와 같이 벡터 자산을 참조 할 수 있습니다 . 앱이 실행되면 해당 벡터 또는 래스터 이미지가 API 수준에 따라 자동으로 표시됩니다.


편집 : 이상한 일이 벌어지고 있습니다. 에뮬레이터 SDK 버전 23의 결과는 다음과 같습니다 (Lollipop + 테스트 장치가 지금 죽었습니다 …).

6.x에서 좋은 종소리

에뮬레이터 SDK 버전 21에서 :

5.x의 크 래피 벨


답변

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

이제 벡터 드로어 블의 크기가 올바르게 조정됩니다!


답변

1) 벡터 드로어 블에 너비 / 높이 사양이있는 이유는 무엇입니까? 나는 이것들의 전체 요점은 그들이 정의에서 너비와 높이를 무의미하게 만드는 무손실로 확장 및 축소된다는 것이라고 생각했습니다.

레이아웃보기에서 정의하지 않은 경우 벡터의 기본 크기입니다. (즉, 이미지 뷰의 높이와 너비에 랩 콘텐츠를 사용합니다)

2) TextView에서 24dp 드로어 블과 큰 화면 너비 이미지로 작동하는 단일 벡터 드로어 블을 사용할 수 있습니까?

예, 가능하며 실행중인 장치가 롤리팝 이상을 사용하는 한 크기 조정에 문제가 없었습니다. 이전 API에서 벡터는 앱을 빌드 할 때 다양한 화면 크기에 대해 png로 변환됩니다.

3) 너비 / 높이 속성을 효과적으로 사용하는 방법과 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하지만 그것을 사용할 수 없습니다. 하지만 트릭 방법을 찾았습니다.

로 드로어 블을 설정 background하여의 ImageView.

그러나 이러한 방식의 요점은 뷰 치수입니다. ImageView치수가 잘못된 경우 드로어 블이 스트레치됩니다. 롤리팝 이전 버전 또는 일부보기 사용에서 제어해야합니다 PercentRelativeLayout.

도움이 되었기를 바랍니다.


답변

먼저 : svg를 드로 블로 가져 오기 : (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1- 드로어 블 폴더를 마우스 오른쪽 버튼으로 클릭하고 새로 만들기-> 벡터 자산을 선택합니다.

여기에 이미지 설명 입력

2- Vector Asset Studio는 내장 머티리얼 아이콘이나 자체 로컬 svg 파일을 선택할 수있는 옵션을 제공합니다. 필요한 경우 기본 크기를 재정의 할 수 있습니다.

여기에 이미지 설명 입력

둘째 : Android API 7+에서 지원을 원하면 Android 지원 라이브러리를 사용해야합니다 (( https://stackoverflow.com/a/44713221/1140304 )).

1- 추가

vectorDrawables.useSupportLibrary = true

build.gradle 파일에.

2-imageView가있는 레이아웃에서 네임 스페이스 사용 :

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

세 번째 : 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" />

넷째 : 자바 코드에서 svg를 설정하려면 oncreate 방법에 아래 줄을 추가해야합니다.

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