Tại sao vectơ của tôi không thể mở rộng tỷ lệ như mong đợi?


97

Tôi đang cố gắng sử dụng vector có thể kéo được trong ứng dụng Android của mình. Từ http://developer.android.com/training/material/drawables.html (tôi nhấn mạnh):

Trong Android 5.0 (API cấp 21) trở lên, bạn có thể xác định các tệp có thể vẽ vectơ, chia tỷ lệ mà không làm mất định nghĩa.

Sử dụng có thể vẽ này:

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

và ImageView này:

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

tạo ra hình ảnh mờ này khi cố gắng hiển thị biểu tượng ở 400dp (trên một thiết bị di động khá lớn chạy kẹo mút vào khoảng năm 2015 có độ phân giải cao):

mờBellIcon

Thay đổi chiều rộng và chiều cao trong định nghĩa của vectơ có thể vẽ thành 200dp cải thiện đáng kể tình hình ở kích thước kết xuất 400dp. Tuy nhiên, việc đặt điều này làm phần tử TextView (tức là biểu tượng ở bên trái văn bản) có thể vẽ được bây giờ sẽ tạo ra một biểu tượng lớn.

Những câu hỏi của tôi:

1) Tại sao lại có thông số chiều rộng / chiều cao trong vector có thể vẽ được? Tôi nghĩ rằng toàn bộ điểm của những điều này là chúng tăng và giảm một cách dễ dàng làm cho chiều rộng và chiều cao trở nên vô nghĩa trong định nghĩa của nó?

2) Có thể sử dụng một vectơ có thể vẽ hoạt động như một tệp có thể vẽ 24dp trên TextView nhưng mở rộng quy mô tốt để sử dụng nhiều hình ảnh lớn hơn không? Ví dụ: làm cách nào để tránh tạo nhiều tệp có thể kéo vectơ với các kích thước khác nhau và thay vào đó sử dụng một bảng có tỷ lệ phù hợp với yêu cầu hiển thị của tôi?

3) Làm cách nào để sử dụng hiệu quả các thuộc tính width / height và sự khác biệt với viewportWidth / Height là gì?

Chi tiết bổ sung:

  • Thiết bị đang chạy API 22
  • Sử dụng Android Studio v1.5.1 với phiên bản Gradle 1.5.0
  • Manifest là biên dịch và mục tiêu cấp 23, cấp tối thiểu 15. Tôi cũng đã thử chuyển cấp tối thiểu lên 21, nhưng điều này không có gì khác biệt.
  • Việc biên dịch APK (với mức tối thiểu được đặt thành 21) hiển thị một tài nguyên XML duy nhất trong thư mục có thể vẽ. Không có hình ảnh nào được tạo ra.

Chỉ để được rõ ràng. Bạn đang sử dụng Android studio? Phiên bản Android Studio nào và phiên bản nào của plugin Gradle? Khi tôi nhấp chuột phải vào thư mục có thể vẽ trong Android Studio và chọn New -> Vector Assetnó sẽ thả XML hình ảnh vectơ vào thư mục có thể vẽ của tôi. Tuy nhiên, nếu tôi sử dụng apktool để giải nén APK đã được tạo, tôi thấy rằng các tệp XML đã ở trong drawable-anydpi-v21và chia tỷ lệ chính xác trên các thiết bị API 21+. Các tập tin raster được đặt trong drawable-<mdpi/hdpi/etc>-v4thư mục và không được sử dụng trên API trên 21 thiết bị (dựa trên thực tế là họ mở rộng quy mô một cách chính xác)
Cory Charlton

@Cory Charlton. Tò mò. Tôi đang sử dụng Studio 1.5.1 với Gradle 1.5.0. Sau khi thay đổi mức tối thiểu thành 21, nơi duy nhất hình ảnh xuất hiện trong APK là drawablethư mục. Chiều rộng / chiều cao trong tệp xml có thể vẽ vector là 24dp. Việc chỉ định ImageView có chiều cao / chiều rộng 400dp chắc chắn tạo ra hình ảnh có tỷ lệ kém.
Chris Knight

Đó là những gì tôi mong đợi. Lý do duy nhất tôi kết thúc drawable-anydpi-v21là vì tôi minSdkVersionnhỏ hơn 21. Có thay đổi hành vi nào nếu bạn đặt thành minSdkVersionnhỏ hơn 21 không? Còn việc chuyển XML sang drawable-anydpithì sao? Tôi sẽ không mong đợi có được một sự thay đổi nhưng tôi cũng mong chờ hình ảnh vector của bạn được mở rộng quy mô một cách chính xác ...
Cory Charlton

Thay đổi phiên bản tối thiểu trở lại 15 sẽ tạo ra kết quả giống như bạn. Một tệp xml duy nhất drawable-anydpi-v21với nhiều hình ảnh được phân loại khác nhau trong mdi / hdpi / etc. thư mục. Không có thay đổi đối với kết quả hiển thị cuối cùng.
Chris Knight

Rất kỳ lạ ... Tôi sửa đổi một trong những ứng dụng của tôi sử dụng hình ảnh của bạn và nó hoạt động tốt (xem câu trả lời đã chỉnh sửa của tôi)
Cory Charlton

Câu trả lời:


100

Có thông tin mới về vấn đề này ở đây:

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

Có vẻ như việc sử dụng android:scaleType="fitXY"sẽ làm cho nó chia tỷ lệ chính xác trên Lollipop.

Từ một kỹ sư của Google:

Xin chào, Hãy cho tôi biết nếu scaleType = 'fitXY' có thể là một giải pháp thay thế cho bạn, để có được hình ảnh sắc nét.

Marshmallow Vs Lollipop là do một phương pháp xử lý đóng cặn đặc biệt được thêm vào marshmallow.

Ngoài ra, đối với nhận xét của bạn: "Hành vi đúng: Vectơ có thể vẽ phải chia tỷ lệ mà không làm giảm chất lượng. Vì vậy, nếu chúng tôi muốn sử dụng cùng một nội dung ở 3 kích thước khác nhau trong ứng dụng của mình, chúng tôi không phải sao chép vector_drawable.xml 3 lần với các kích thước mã cứng. "

Mặc dù tôi hoàn toàn đồng ý rằng điều này nên xảy ra, trên thực tế, nền tảng Android có mối quan tâm về hiệu suất khiến chúng ta chưa đạt được thế giới lý tưởng. Vì vậy, bạn thực sự nên sử dụng 3 vector_drawable.xml khác nhau để có hiệu suất tốt hơn nếu bạn chắc chắn muốn vẽ 3 kích thước khác nhau trên màn hình cùng một lúc.

Chi tiết kỹ thuật về cơ bản là chúng ta đang sử dụng một bitmap dưới móc để lưu vào bộ nhớ cache kết xuất đường dẫn phức tạp, để chúng ta có thể có được hiệu suất vẽ lại tốt nhất, ngang bằng với việc vẽ lại một bitmap có thể vẽ được.


27
Được rồi, Google, thật khủng khiếp khi chúng ta phải sao chép và dán các bảng vẽ giống hệt nhau với cùng một khung nhìn và đường dẫn chỉ có kích thước khác nhau.
Miha_x64

Điều này đã khắc phục sự cố trên thiết bị đang hiển thị logo của tôi được pixel hóa, nhưng trên thiết bị khác, nơi mọi thứ đều ổn trước đây, thì giờ đó là tỷ lệ khung hình sai. Tôi không hiểu tại sao đây là một vấn đề, nó là một vector! Không phải pixel! : P
tplive

@Harish Gyanani, android: scaleType = "fitXY" giải quyết được vấn đề nhưng còn các biểu tượng động và không bình phương thì sao?
Manuela

28

1) Tại sao lại có thông số chiều rộng / chiều cao trong vector có thể vẽ được? Tôi nghĩ rằng toàn bộ điểm của những điều này là chúng tăng và giảm một cách dễ dàng làm cho chiều rộng và chiều cao trở nên vô nghĩa trong định nghĩa của nó?

Đối với các phiên bản SDK nhỏ hơn 21 trong đó hệ thống xây dựng cần tạo hình ảnh raster và làm kích thước mặc định trong trường hợp bạn không chỉ định chiều rộng / chiều cao.

2) Có thể sử dụng một vectơ có thể vẽ hoạt động như một tệp có thể vẽ 24dp trên TextView cũng như hình ảnh có chiều rộng gần màn hình lớn không?

Tôi không tin rằng điều này là khả thi nếu bạn cũng cần nhắm mục tiêu SDK nhỏ hơn 21.

3) Làm cách nào để sử dụng hiệu quả các thuộc tính width / height và sự khác biệt với viewportWidth / Height là gì?

Tài liệu: (thực sự không hữu ích lắm khi tôi đọc lại nó ...)

android:width

Được sử dụng để xác định chiều rộng nội tại của có thể vẽ. Điều này hỗ trợ tất cả các đơn vị thứ nguyên, thường được chỉ định bằng dp.

android:height

Được sử dụng để xác định chiều cao nội tại có thể vẽ. Điều này hỗ trợ tất cả các đơn vị thứ nguyên, thường được chỉ định bằng dp.

android:viewportWidth

Được sử dụng để xác định chiều rộng của không gian khung nhìn. Viewport về cơ bản là canvas ảo nơi các đường dẫn được vẽ trên đó.

android:viewportHeight

Được sử dụng để xác định chiều cao của không gian khung nhìn. Viewport về cơ bản là canvas ảo nơi các đường dẫn được vẽ trên đó.

Tài liệu khác:

Android 4.4 (API cấp 20) trở xuống không hỗ trợ các tệp có thể kéo vectơ. Nếu mức API tối thiểu của bạn được đặt ở một trong các mức API này, Vector Asset Studio cũng chỉ đạo Gradle tạo hình ảnh raster của vector có thể vẽ để tương thích ngược. Bạn có thể tham khảo các tài sản vectơ như Drawabletrong mã Java hoặc @drawabletrong mã XML; khi ứng dụng của bạn chạy, hình ảnh vectơ hoặc đường raster tương ứng sẽ tự động hiển thị tùy thuộc vào cấp API.


Chỉnh sửa: Có điều gì đó kỳ lạ đang xảy ra. Đây là kết quả của tôi trong SDK trình giả lập phiên bản 23 (thiết bị thử nghiệm Lollipop + hiện đã chết ...):

Chuông tốt trên 6.x

Và trong SDK trình giả lập phiên bản 21:

Chuông nứt trên 5.x


1
Cảm ơn Cory, tôi đã xem tài liệu đó và cũng thấy nó không hữu ích lắm. Thiết bị của tôi là Lollipop (v22) và tôi không thể chia tỷ lệ đồ họa tốt, trên 24dp được chỉ định trong vector có thể vẽ, bất kể chiều cao / trọng lượng có được chỉ định chính xác trong ImageView hay không. Tôi đã thử nghiệm một tệp kê khai với mục tiêu và biên dịch cấp 23 và tối thiểu cấp 21 nhưng nó sẽ không dễ dàng nâng cấp vectơ có thể vẽ của tôi nếu tôi không thay đổi chiều rộng / chiều cao trong chính tệp có thể vẽ. Tôi không thực sự muốn tạo nhiều ngăn kéo có sự khác biệt duy nhất là chiều cao / chiều rộng!
Chris Knight

1
Cảm ơn đã xác nhận và thực sự đánh giá cao sự giúp đỡ của bạn. Như bạn đã chứng minh, nó rõ ràng sẽ hoạt động như tôi mong đợi. Sử dụng mã chính xác của bạn tạo ra kết quả giống như tôi đã có ban đầu. Bực bội!
Chris Knight

1
CẬP NHẬT: Chạy mã của tôi so với trình giả lập với API 22 và tôi vẫn nhận được kết quả tương tự. Chạy mã của tôi chống lại trình giả lập với API 23 và, tada !, nó hoạt động. Có vẻ như nâng cấp chỉ hoạt động trên các thiết bị API 23+. Đã cố gắng thay đổi phiên bản SDK mục tiêu nhưng điều đó không có gì khác biệt.
Chris Knight

Bạn đã tìm ra giải pháp cho vấn đề này chưa?
dazza5000

@corycharlton có thể xóa width height viewport heightwidthkhỏi xmlkhông?
VINNUSAURUS

9

AppCompat 23.2 giới thiệu các vector có thể kéo được cho tất cả các thiết bị chạy Android 2.1 trở lên. Hình ảnh chia tỷ lệ chính xác, không theo quy luật của chiều rộng / chiều cao được chỉ định trong tệp XML của vector có thể vẽ. Rất tiếc, triển khai này không được sử dụng trong API cấp 21 trở lên (có lợi cho triển khai gốc).

Tin tốt là chúng ta có thể buộc AppCompat sử dụng cách triển khai của nó trên API cấp 21 và 22. Tin xấu là chúng ta phải sử dụng phản chiếu để thực hiện điều này.

Trước hết, hãy đảm bảo rằng bạn đang sử dụng phiên bản mới nhất của AppCompat và bạn đã bật triển khai vectơ mới:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

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

Sau đó, gọi useCompatVectorIfNeeded();trong onCreate () của Ứng dụng của bạn:

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

Cuối cùng, hãy đảm bảo rằng bạn đang sử dụng app:srcCompatthay vì android:srcđặt hình ảnh của ImageView:

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

Các bảng vẽ vector của bạn bây giờ sẽ chia tỷ lệ chính xác!


Cách tiếp cận tốt, nhưng, AppCompat 25.4 (và có thể sớm hơn) đưa ra lỗi lint "chỉ có thể được gọi từ cùng một nhóm thư viện". Các công cụ xây dựng tôi đang sử dụng vẫn cho phép nó xây dựng, nhưng tôi không chắc liệu có hậu quả về thời gian chạy hay không. Sắp thử nghiệm.
androidguy

chỉ có thể được gọi từ nhóm cùng một thư viện, loại bỏ lỗi này bằng cách thêm này: lintOptions {vô hiệu hoá 'RestrictedApi'}
Usama Saeed Mỹ

6

1) Tại sao lại có thông số chiều rộng / chiều cao trong vector có thể vẽ được? Tôi nghĩ rằng toàn bộ điểm của những điều này là chúng tăng và giảm một cách dễ dàng làm cho chiều rộng và chiều cao trở nên vô nghĩa trong định nghĩa của nó?

Đây chỉ là kích thước mặc định của vectơ trong trường hợp bạn không xác định nó trong dạng xem bố cục. (tức là Bạn sử dụng nội dung bao bọc cho chiều cao và chiều rộng của chế độ xem hình ảnh của bạn)

2) Có thể sử dụng một vectơ có thể vẽ hoạt động như một tệp có thể vẽ 24dp trên TextView cũng như hình ảnh có chiều rộng gần màn hình lớn không?

Có, Có thể và tôi không gặp vấn đề gì với việc thay đổi kích thước miễn là thiết bị đang chạy sử dụng kẹo mút hoặc cao hơn. Trong các API trước đây, vectơ được chuyển đổi thành pngs cho các kích thước màn hình khác nhau khi bạn tạo ứng dụng.

3) Làm cách nào để sử dụng hiệu quả các thuộc tính width / height và sự khác biệt với viewportWidth / Height là gì?

Điều này ảnh hưởng đến cách sử dụng không gian xung quanh vectơ của bạn. tức là Bạn có thể sử dụng nó để thay đổi "trọng lực" của vectơ bên trong khung nhìn, làm cho vectơ có lề mặc định, để các phần nhất định của vectơ ra khỏi chế độ xem, v.v. Thông thường, bạn chỉ cần đặt chúng như nhau kích thước.


Cảm ơn Emiura. Tôi muốn quan tâm đến cách bạn đạt được nhiều kích thước được kết xuất bằng cách sử dụng cùng một loại có thể kéo được. Bằng cách sử dụng có thể vẽ 24dp (cho mục đích thử nghiệm), tôi đã thay đổi ImageView của mình thành có chiều rộng và chiều cao được chỉ định là 400dp và chạy trên thiết bị Lollipop của tôi (v22) nhưng nó vẫn hiển thị kém, giống như một hình ảnh được nâng cấp theo tỉ lệ hơn là một hình ảnh vectơ. Cũng đã thay đổi tệp kê khai của tôi để có mục tiêu và biên dịch dựa trên phiên bản v23 và min 21. Không có sự khác biệt.
Chris Knight

Trong trường hợp này, bạn chỉ cần sử dụng kích thước lớn nhất trong vector có thể vẽ được và sau đó chỉ định kích thước 24dp trong chế độ xem văn bản của bạn.
emirua

Tôi thấy rằng bây giờ bạn nhận được hình ảnh mờ khi bạn phóng to với sự khác biệt rất lớn giữa kích thước trong vector có thể vẽ và kích thước trong bố cục trong Lollipop. Tuy nhiên, nó vẫn gọn gàng hơn rất nhiều khi chỉ đặt kích thước lớn nhất thay vì có nhiều tệp cho các kích thước màn hình khác nhau;).
emirua

4

Tôi giải quyết vấn đề của mình chỉ cần thay đổi kích thước của hình ảnh vector. Từ đầu tiên, nó là tài nguyên như:

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

Và tôi đã thay đổi nó thành 150dp (kích thước của ImageView trong bố cục với tài nguyên vectơ này) như:

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

Nó làm việc cho tôi.


cảm ơn, điều này là đủ để giải quyết vấn đề trên máy tính bảng của tôi, nơi nó xuất hiện pixel.
user2342558

3

Tôi phải thử những cách này nhưng không may, không có cách nào trong số chúng hiệu quả. Tôi thử fitXYvào ImageView, tôi thử sử dụng app:srcCompatnhưng thậm chí không thể sử dụng nó. nhưng tôi đã tìm ra một mẹo nhỏ:

đặt Drawable thành backgroundcủa bạn ImageView.

Nhưng điểm của cách này là các thứ nguyên Lượt xem. nếu bạn ImageViewcó kích thước không chính xác, có thể kéo được Kéo dài. bạn phải kiểm soát nó trong các phiên bản trước lollipop hoặc Sử dụng một số Chế độ xem PercentRelativeLayout.

Hy vọng nó hữu ích.


2

Đầu tiên : nhập svg vào drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Nhấp chuột phải vào thư mục có thể vẽ chọn Mới -> Nội dung Vector

nhập mô tả hình ảnh ở đây

2- Vector Asset Studio cung cấp cho bạn tùy chọn để chọn các biểu tượng Vật liệu có sẵn hoặc tệp svg cục bộ của riêng bạn. Bạn có thể ghi đè kích thước mặc định nếu cần.

nhập mô tả hình ảnh ở đây

Thứ hai : nếu bạn muốn được hỗ trợ từ Android API 7+, bạn phải sử dụng bản ghi Thư viện hỗ trợ Android (( https://stackoverflow.com/a/44713221/1140304 ))

1- thêm

vectorDrawables.useSupportLibrary = true

vào tệp build.gradle của bạn.

2-Sử dụng không gian tên trong bố cục của bạn có imageView:

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

Thứ ba : đặt imageView với android: scaleType = "fitXY" và sử dụng ứng dụng: srcCompat

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

Thứ tư : nếu bạn muốn đặt svg trong mã java, bạn phải thêm dòng dưới đây trên oncreate methoed

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

Giải pháp của bạn có hoạt động trên API 21 trở lên không? user3210008 ở trên đề cập rằng việc triển khai không được sử dụng trên API 21 trở lên.
AJW

2

Đối với tôi, lý do khiến vectơ được chuyển đổi thành png là android:fillType="evenodd"thuộc tính trong vectơ có thể vẽ của tôi. Khi tôi xóa tất cả các lần xuất hiện của thuộc tính này trong tệp có thể vẽ của mình (đối với nonzerokiểu điền mặc định được áp dụng cho vectơ), lần sau khi ứng dụng được tạo, mọi thứ đều ổn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.