Công thức px đến dp, dp sang px android


150

Tôi đang cố gắng tính toán một lượng pixel thay đổi theo mật độ pixel độc lập và ngược lại.

Công thức (px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));này không hoạt động trên các thiết bị nhỏ vì nó được chia cho số không.

Đây là công thức dp to px của tôi:

px = (int)(dp * (displayMetrics.densityDpi / 160));

Ai đó có thể cho tôi một số gợi ý?


1
Chuyển đổi các đơn vị dp thành đơn vị pixel developer.android.com/guide/practices/
trộm

@Bram: Tôi nghĩ rằng công thức của bạn là tốt. Làm thế nào bạn sẽ có được một phân chia bằng không? displayMetrics.d mậtDpi sẽ là 120, 160, 240 hoặc 320, không bao giờ 0.
ct_rob

Tôi đồng ý với @ct_rob. giá trị tối thiểu displayMetrics.d mậtDpi / 160 sẽ là 0,75. Bạn phải được truyền đến int ở vị trí không chính xác.
TomTaila

Câu trả lời:


318

Lưu ý: Giải pháp được sử dụng rộng rãi ở trên dựa trên displayMetrics.density. Tuy nhiên, các tài liệu giải thích rằng giá trị này là giá trị làm tròn, được sử dụng với màn hình 'xô'. Ví dụ. trên Nexus 10 của tôi, nó trả về 2, trong đó giá trị thực sẽ là 298dpi (thực) / 160dpi (mặc định) = 1.8625.

Tùy thuộc vào yêu cầu của bạn, bạn có thể cần chuyển đổi chính xác, có thể đạt được như thế này:

[Chỉnh sửa] Điều này không có nghĩa là được trộn lẫn với đơn vị dp nội bộ của Android, vì điều này tất nhiên vẫn dựa trên các xô màn hình. Sử dụng điều này khi bạn muốn một đơn vị sẽ hiển thị cùng kích thước thực trên các thiết bị khác nhau.

Chuyển đổi dp thành pixel:

public int dpToPx(int dp) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));     
}

Chuyển đổi pixel sang dp:

public int pxToDp(int px) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

Lưu ý rằng có các thuộc tính xdpi và ydpi, bạn có thể muốn phân biệt, nhưng tôi không thể tưởng tượng một màn hình lành mạnh trong đó các giá trị này khác nhau rất nhiều.


8
Điều này sẽ không tính giá trị chính xác cho dp / px trên nhiều thiết bị (bao gồm cả Nexus 10 của bạn)! Như bạn nói displayMetrics.d mật được làm tròn đến nhóm màn hình gần nhất, nhưng đơn vị dp cũng vậy! Hãy thử vẽ một đối tượng rộng 160dp và ngay bên dưới nó, bạn vẽ một đối tượng khác có chiều rộng dpToPx (160) pixel và bạn sẽ thấy kích thước của hai đối tượng là khác nhau. Ngoài ra, một số điện thoại (như Galaxy Mini và Galaxy S3 Mini) báo cáo các giá trị sai hoàn toàn cho xdpi / ydpi, vì vậy trên các điện thoại này, các phương thức của bạn sẽ trả về kết quả hoàn toàn sai.
nibarius

@nibarius Có, bạn không thể trộn các tính toán dp đóng hộp của Android với các tính toán trên. Giải pháp trên có nghĩa là một giá trị độc lập mật độ riêng biệt dựa trên vật lý thiết bị chính xác. Cần thiết, ví dụ, nơi bạn muốn hiển thị một dòng có độ dài chính xác giống nhau trên các thiết bị khác nhau. Tất nhiên, nếu các đầu vào xdpi / ydpi không được đặt chính xác bởi một số thiết bị, nó sẽ không hoạt động ở đó.
Bachi

1
xdpi và ydpi không nên được sử dụng, vì chúng không chính xác trên nhiều thiết bị, đôi khi rất nhiều. Chỉ DisplayMetrics.d mậtDpi là đáng tin cậy, điều này thật đáng tiếc, vì nó không chính xác bởi thiết kế. Xem chủ đề diễn đàn của Google để biết thêm thông tin: Groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland

1
Tôi đã tìm thấy phản hồi này và thực sự đã sử dụng nhiều giải pháp tương tự nhưng tôi chỉ xem qua các tài liệu và tìm thấy getDimensionPixel Offerset với một thứ nguyên (được khai báo bằng dip / dp) trả về offset theo pixel giống như mã thủ công. Tôi đã thử nghiệm và làm việc hoàn hảo. Hy vọng nó giúp!
william gouvea

1
Mehhh, điều này không cung cấp giá trị chính xác. Hãy thử: Tài nguyên r = getResource (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); Như được mô tả ở đây: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

Tôi đã giải quyết vấn đề của mình bằng cách sử dụng các công thức sau. Có thể những người khác được hưởng lợi từ nó.

dp đến px:

displayMetrics = context.getResources().getDisplayMetrics();
return (int)((dp * displayMetrics.density) + 0.5);

px đến dp:

displayMetrics = context.getResources().getDisplayMetrics();
return (int) ((px/displayMetrics.density)+0.5);

27
@Vame Việc thêm 0,5 được sử dụng để làm tròn LÊN đến giá trị số nguyên gần nhất .. 0,5 được thêm vào và sau đó kết quả của phép tính được tạo thành một int khiến nó cắt ngắn phần mantissa và để lại đặc tính là một giá trị số nguyên được làm tròn đúng.
Kelly Copley

3
Điều này không phải lúc nào cũng đúng. Khi tôi có hai bố cục một bên trong nhau và sau đó tôi làm tròn các góc của mỗi chế độ xem (một bằng cách sử dụng dp, các góc khác chuyển sang dp) không khớp!
Marek

Tôi không có bất kỳ vấn đề với kỹ thuật này. Tôi đã sử dụng điều này cho các bố cục khác nhau và nó luôn hoạt động như mong đợi. Tất nhiên tôi đã sử dụng điều này vài năm trước và tôi không chắc liệu nó có còn hoạt động không. Có lẽ bạn có thể thử kỹ thuật khác trong câu trả lời của PanaVTEC. Cũng có thể có nhiều góc làm tròn hơn là tính toán dp / px.
Bram

5
Đó là giải pháp tương tự được áp dụng trên trang web dành cho nhà phát triển Android, vì vậy tôi đoán đó là chính xác :). http://developer.android.com/guide/practices/sc
Greens_support.html#dips

1
+1 Cảm ơn người đàn ông. Làm việc như người ở! Cảm ơn bạn đã chia sẻ giải pháp này với chúng tôi.
Simon Dorociak

34

px đến dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());

7
Đây là từ DP đến PX, nhưng với sự điều chỉnh này: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());là từ PX đến DP, đây cũng là câu trả lời hay nhất
PaNaVTEC

1
@PaNaVTEC, giải pháp bạn tham chiếu cho dp đến px là không chính xác. applyDimensionchỉ mang lại pixel. Xem android.googlesource.com/pl platform / frameworks / base / + / refs / Heads / ' trong đó không có chuyển đổi nào được thực hiện cho COMPLEX_UNIT_PX.
Stephen Niedzielski

Câu trả lời này là hoàn toàn sai. Nó trả về giá trị cuối cùng bằng pixel, không bao giờ trong dp. Nếu bạn nhìn vào mã nguồn, đối với trường hợp TypedValue.COMPLEX_UNIT_DIP, value * metrics.densityđược trả về, nơi bạn thực sự cần value * metrics.density. Vì vậy, những gì bạn đang getting` valueInDp` là KHÔNG trong dpcho valueInPxtrong px.
bitbybit

^ typo, ý tôi là "... nơi bạn thực sự cần value / metrics.density"
bitbybit

31

Cách hiệu quả

DP sang Pixel:

private int dpToPx(int dp)
{
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

Pixel đến DP:

private int pxToDp(int px)
{
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

Hy vọng điều này sẽ giúp bạn.


4
Tốt hơn vì không cần sử dụng Bối cảnh :) Cảm ơn.
Ayaz Alifov

2
Câu trả lời tốt nhất! Thật không may, câu trả lời "Đánh dấu là chính xác" là sai. Đặc biệt là vì nó sử dụng displayMetrics.xdpikhác nhau trên mọi thiết bị. Đáng buồn thay, câu trả lời sai này cũng có nhiều upvote nhất.
toom

đẹp. nên được đánh dấu là câu trả lời tốt nhất. kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

Chỉ cần gọi getResources().getDimensionPixelSize(R.dimen.your_dimension)để chuyển đổi từ dpđơn vị sang pixel


3
Theo tài liệu, điều này giống như getDimension (), ngoại trừ giá trị được trả về được chuyển đổi thành pixel nguyên để sử dụng làm kích thước. Chuyển đổi kích thước bao gồm làm tròn giá trị cơ sở và đảm bảo rằng giá trị cơ sở khác không có ít nhất một pixel có kích thước.
CJBS

14

Sử dụng chức năng này

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

mặc dù, bây giờ tôi nghĩ về nó ... làm thế nào sẽ có một phép chia bằng 0 trong công thức gốc của Brams? displayMetrics.d mậtDpi sẽ là 120, 160, 240 hoặc 320, không bao giờ 0.
ct_rob

2
(displayMetrics.densityDpi / 160)- phần này có thể nhận 0 trên các thiết bị nhỏ, ở đó nó tính toán 120/160, cả hai giá trị là int, kết quả là 0. Kết quả là (int) (px/0).

à, bạn nói đúng Vì vậy, tất cả những gì anh ta cần làm là sử dụng lâu dài trong công thức của mình: 160.0
ct_rob

câu trả lời này tương tự câu trả lời trên vì DisplayMetrics.DENSITY_DEFAULT có 160 giá trị. nhưng bạn có thể vui lòng cho chúng tôi biết DPI ở đây là cách chúng tôi tính toán nó không.
John smith

3

Trong hầu hết các trường hợp, các hàm chuyển đổi được gọi thường xuyên. Chúng ta có thể tối ưu hóa nó bằng cách thêm ghi nhớ. Vì vậy, nó không tính toán mỗi lần hàm được gọi.

Hãy khai báo HashMap sẽ lưu trữ các giá trị được tính toán.

private static Map<Float, Float> pxCache = new HashMap<>();

Một hàm tính toán các giá trị pixel:

public static float calculateDpToPixel(float dp, Context context) {

        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float px = dp * (metrics.densityDpi / 160f);
        return px;

    }

Hàm ghi nhớ trả về giá trị từ HashMap và duy trì bản ghi các giá trị trước đó.

Ghi nhớ có thể được thực hiện theo các cách khác nhau trong Java. Đối với Java 7 :

public static float convertDpToPixel(float dp, final Context context) {

        Float f = pxCache.get(dp);
        if (f == null) {
            synchronized (pxCache) {
                f = calculateDpToPixel(dp, context);
                pxCache.put(dp, f);
            }

        }

        return f;
    }

Java 8 hỗ trợ chức năng Lambda :

public static float convertDpToPixel(float dp, final Context context) {

        pxCache.computeIfAbsent(dp, y ->calculateDpToPixel(dp,context));
}

Cảm ơn.


bổ sung tốt đẹp cho các giải pháp nhất định.
Bram

Tôi sẽ ngạc nhiên nếu tính toán chuyển đổi ít nhất là nhanh như tra cứu bản đồ, chưa kể đến việc tính toán đồng bộ hóa. Bạn có bất kỳ kết quả thử nghiệm để chứng minh tối ưu hóa này là có lợi? Trên Android nơi bộ nhớ làm việc bị hạn chế, bộ nhớ giao dịch cho nỗ lực tính toán không phải là điều bạn nên làm mà không có lý do chính đáng.
Lorne Laliberte


2

Dưới đây funtions hoạt động tốt cho tôi trên các thiết bị.

Nó được lấy từ https://gist.github.com/laaptu/7867851

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}

1

Bạn có thể sử dụng [DisplayMatrics][1]và xác định mật độ màn hình. Một cái gì đó như thế này:

int pixelsValue = 5; // margin in pixels
float d = context.getResources().getDisplayMetrics().density;
int margin = (int)(pixelsValue * d);

Theo tôi nhớ, tốt hơn là sử dụng sàn để bù đắp và làm tròn cho chiều rộng.



1

// để nhận được về số thập phân / Float

public static float convertPixelsToDp(float px, Context context) {

    Resources resources = context.getResources();
    DisplayMetrics metrics = resources.getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}



public static float convertDpToPixel(float dp, Context context) {
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


// for  getting in terms of Integer

private int convertPxToDp(int px, Context context) {
    Resources resources = context.getResources();
    return Math.round(px / (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
}


private int convertDpToPx(int dp, Context context) {
    Resources resources = context.getResources();

    return Math.round(dp * (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));

}

Kể từ khi bắt đầu

public static float convertPixelsToDp(float px){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float dp = px / (metrics.densityDpi / 160f);
    return Math.round(dp);
}

public static float convertDpToPixel(float dp){
    DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
    float px = dp * (metrics.densityDpi / 160f);
    return Math.round(px);
}


private int convertDpToPx(int dp){
    return Math.round(dp*(getResources().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));

}

private int convertPxToDp(int px){
    return Math.round(px/(Resources.getSystem().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));
}

1

Sử dụng các tiện ích mở rộng Kotlin này:

/**
 * Converts Pixel to DP.
 */
val Int.pxToDp: Int
    get() = (this / Resources.getSystem().displayMetrics.density).toInt()

/**
 * Converts DP to Pixel.
 */
val Int.dpToPx: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

0

Hãy sử dụng phương pháp này tôi đã viết:

int dpToPx(int dp)
{
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}

0

Câu trả lời được chấp nhận ở trên là không hoàn toàn chính xác. Theo thông tin có được bằng cách kiểm tra mã nguồn Android:

Resources.getDimension()getDimensionPixelOffset()/ getDimensionPixelSize()chỉ khác nhau ở chỗ cái trước trả về floattrong khi hai cái sau trả về cùng một giá trị được làm tròn thành intthích hợp. Đối với tất cả chúng, giá trị trả về được tính bằng pixel thô.

Tất cả ba chức năng là implementedy bằng cách gọi Resources.getValue()và chuyển đổi do đó thu được TypedValuebằng cách gọi TypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()TypedValue.complexToDimensionPixelSize() , tương ứng.

Do đó, nếu bạn muốn có được giá trị "thô" cùng với đơn vị được chỉ định trong nguồn XML, hãy gọi Resources.getValue()và sử dụng các phương thức của TypedValuelớp.


0

với sự giúp đỡ của các câu trả lời khác, tôi đã viết chức năng này.

public static int convertToPixels(Context context, int nDP)
{
        final float conversionScale = context.getResources().getDisplayMetrics().density; 
        return (int) ((nDP * conversionScale) + 0.5f) ;
}

0

Đây là một cách khác để làm điều đó bằng cách sử dụng các phần mở rộng kotlin:

val Int.dpToPx: Int
    get() = Math.round(this * Resources.getSystem().displayMetrics.density)

val Int.pxToDp: Int
    get() = Math.round(this / Resources.getSystem().displayMetrics.density)

và sau đó nó có thể được sử dụng như thế này từ bất cứ đâu

12.dpToPx

244.pxToDp

0
DisplayMetrics displayMetrics = contaxt.getResources()
            .getDisplayMetrics();

    int densityDpi = (int) (displayMetrics.density * 160f);
    int ratio = (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    int px;
    if (ratio == 0) {
        px = dp;
    } else {
        px = Math.round(dp * ratio);

    }

0

biến thể trên câu trả lời ct_robs ở trên, nếu bạn đang sử dụng số nguyên, không chỉ tránh chia cho 0, nó còn tạo ra kết quả có thể sử dụng trên các thiết bị nhỏ:

trong các phép tính số nguyên liên quan đến phép chia cho độ chính xác lớn nhất nhân trước tiên trước khi chia để giảm hiệu ứng cắt ngắn.

px = dp * dpi / 160
dp = px * 160 / dpi

5 * 120 = 600 / 160 = 3

thay vì

5 * (120 / 160 = 0) = 0

nếu bạn muốn kết quả tròn làm điều này

px = (10 * dp * dpi / 160 + 5) / 10
dp = (10 * px * 160 / dpi + 5) / 10

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
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.