สูตร px เป็น dp, dp ถึง px android


150

ฉันกำลังพยายามคำนวณจำนวนพิกเซลของพิกเซลเพื่อความหนาแน่นของพิกเซลอิสระและในทางกลับกัน

สูตรนี้ใช้(px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160));ไม่ได้กับอุปกรณ์ขนาดเล็กเนื่องจากถูกหารด้วยศูนย์

นี่คือสูตร dp ถึง px ของฉัน:

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

ใครช่วยชี้ให้ฉันได้บ้าง


1
การแปลงหน่วย dp ไปเป็นหน่วยพิกเซลdeveloper.android.com/guide/practices/…
Padma Kumar

@Bram: ฉันคิดว่าสูตรของคุณไม่เป็นไร คุณจะได้รับการหารด้วยศูนย์ได้อย่างไร? displayMetrics.densityDpi จะเป็น 120, 160, 240 หรือ 320, ไม่เคย 0
ct_rob

ฉันเห็นด้วยกับ @ct_rob displayMetrics.densityDpi / 160 ค่าต่ำสุดจะเท่ากับ 0.75 คุณต้องส่งไปยังสถานที่ที่ไม่ถูกต้อง
TomTaila

คำตอบ:


318

หมายเหตุ: displayMetrics.densityการแก้ปัญหาการใช้กันอย่างแพร่หลายดังกล่าวข้างต้นจะขึ้นอยู่กับ อย่างไรก็ตามเอกสารอธิบายว่าค่านี้เป็นค่าปัดเศษที่ใช้กับหน้าจอ 'ที่เก็บข้อมูล' เช่น. บน Nexus 10 ของฉันมันจะคืนค่า 2 โดยที่มูลค่าจริงจะเป็น 298dpi (จริง) / 160dpi (ค่าเริ่มต้น) = 1.8625

ขึ้นอยู่กับความต้องการของคุณคุณอาจต้องการการแปลงที่แน่นอนซึ่งสามารถทำได้ดังนี้:

[แก้ไข] สิ่งนี้ไม่ได้มีไว้เพื่อผสมกับหน่วย dp ภายในของ Android เนื่องจากแน่นอนว่ายังคงใช้ถังหน้าจอ ใช้สิ่งนี้ในที่ที่คุณต้องการหน่วยที่ควรแสดงผลขนาดจริงบนอุปกรณ์ที่แตกต่างกัน

แปลง dp เป็นพิกเซล:

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

แปลงพิกเซลเป็น dp:

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

โปรดทราบว่ามีคุณสมบัติ xdpi และ ydpi คุณอาจต้องการแยกความแตกต่าง แต่ฉันไม่สามารถจินตนาการการแสดงผลที่มีค่าที่แตกต่างกันอย่างมาก


8
สิ่งนี้จะไม่คำนวณค่าที่ถูกต้องสำหรับ dp / px บนอุปกรณ์จำนวนมาก (รวมถึง Nexus 10)! อย่างที่คุณบอกว่า displayMetrics.density จะถูกปัดให้ใกล้กับที่เก็บข้อมูลหน้าจอที่ใกล้ที่สุด แต่จะเป็นหน่วย dp! ลองวาดวัตถุหนึ่งที่มีความกว้าง 160dp และด้านล่างคุณวาดวัตถุอื่นที่มีความกว้างของพิกเซล dpToPx (160) และคุณจะเห็นว่าขนาดของวัตถุทั้งสองนั้นแตกต่างกัน โทรศัพท์บางรุ่น (เช่น Galaxy Mini และ Galaxy S3 Mini) รายงานค่าผิดอย่างสมบูรณ์สำหรับ xdpi / ydpi ดังนั้นในโทรศัพท์ของคุณวิธีการของคุณจะให้ผลลัพธ์ที่ผิดอย่างสมบูรณ์
nibarius

@nibarius ใช่คุณไม่สามารถผสมการคำนวณ dp ของ Android เข้ากับข้างต้น วิธีการแก้ปัญหาข้างต้นหมายถึงค่าความหนาแน่นอิสระแยกต่างหากตามฟิสิกส์ของอุปกรณ์ที่แน่นอน ต้องการเช่นที่ที่คุณต้องการแสดงบรรทัดที่มีความยาวจริงเท่ากันบนอุปกรณ์ต่าง ๆ แน่นอนว่าหากอุปกรณ์บางตัวไม่สามารถตั้งค่าอินพุต xdpi / ydpi ได้อย่างถูกต้องอุปกรณ์จะไม่ทำงานที่นั่น
Bachi

1
ไม่ควรใช้ xdpi และ ydpi เนื่องจากมีความผิดพลาดในอุปกรณ์จำนวนมากบางครั้งก็มาก DisplayMetrics.densityDpi มีความน่าเชื่อถือเท่านั้นซึ่งเป็นเรื่องที่โชคไม่ดีเนื่องจากมีการออกแบบที่ไม่ชัดเจน ดูหัวข้อฟอรัมของ Google สำหรับข้อมูลเพิ่มเติม: groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
Mark McClelland

1
ฉันพบการตอบสนองนี้และใช้วิธีแก้ปัญหาที่คล้ายกันจริง ๆ แต่ฉันเพิ่งผ่านเอกสารและพบ getDimensionPixelOffSet ด้วยมิติที่กำหนด ฉันทดสอบและทำงานได้อย่างไร้ที่ติ หวังว่ามันจะช่วย!
william gouvea

1
Mehhh นี้ไม่ได้ให้ค่าที่ถูกต้อง ลอง: ทรัพยากร r = getResources (); float px = TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics ()); ตามที่อธิบายไว้ที่นี่: stackoverflow.com/questions/4605527/converting-pixels-to-dp
Rik van Velzen

103

ฉันแก้ไขปัญหาโดยใช้สูตรต่อไปนี้ คนอื่นอาจได้รับประโยชน์จากมัน

dp ถึง px:

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

px ถึง dp:

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

27
@Vame การเพิ่ม 0.5 ถูกใช้เพื่อปัดเศษขึ้นเป็นค่าจำนวนเต็มที่ใกล้เคียงที่สุด .. 0.5 ถูกเพิ่มเข้ามาและจากนั้นผลลัพธ์ของการคำนวณจะถูกนำไปใช้เป็น int ทำให้เกิดการตัดทอน mantissa และทำให้มีลักษณะเป็นค่าจำนวนเต็มที่ถูกต้อง
Kelly Copley

3
สิ่งนี้ไม่ถูกต้องเสมอไป เมื่อฉันมีเลย์เอาต์สองแบบในอีกมุมหนึ่งและจากนั้นฉันปัดมุมของแต่ละมุมมอง (อันหนึ่งโดยใช้ dp มุมอื่น ๆ ที่เปลี่ยนเป็น dp) ไม่ตรงกัน!
Marek

ฉันไม่มีปัญหากับเทคนิคนี้ ฉันใช้สิ่งนี้กับเลย์เอาต์ที่แตกต่างกันและมันก็ใช้งานได้ตามปกติ แน่นอนว่าฉันใช้เมื่อไม่กี่ปีที่ผ่านมาและฉันก็ไม่แน่ใจว่ามันยังใช้งานได้หรือไม่ บางทีคุณอาจลองเทคนิคอื่น ๆ ในคำตอบของ PanaVTEC อาจเป็นไปได้ว่ามีการปัดเศษมากกว่าการคำนวณ dp / px
Bram

5
เป็นโซลูชั่นเดียวกับที่ใช้กับเว็บไซต์นักพัฒนาซอฟต์แวร์ Android ดังนั้นฉันเดาว่ามันถูกต้อง :) http://developer.android.com/guide/practices/screens_support.html#dips-pels
alocaly

1
+1 ขอบคุณชาย ทำงานเหมือนจับใจ! ขอบคุณที่แบ่งปันโซลูชันนี้กับเรา
Simon Dorociak

34

px ถึง dp:

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

7
นี่คือจาก DP ถึง PX แต่ด้วยการแก้ไขนี้: typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics());มาจาก PX ถึง DP และนี่เป็นคำตอบที่ดีที่สุด
PaNaVTEC

1
@PaNaVTEC โซลูชันที่คุณอ้างอิงถึง dp ถึง px ไม่ถูกต้อง applyDimensionให้ผลผลิตพิกเซลเท่านั้น ดูandroid.googlesource.com/platform/frameworks/base/+/refs/heads/...COMPLEX_UNIT_PXนั้นไม่มีการแปลงจะดำเนินการสำหรับ
Stephen Niedzielski

คำตอบนี้ผิดธรรมดา ก็จะส่งกลับค่าสุดท้ายในพิกเซล, dpไม่เคยอยู่ใน ถ้าคุณดูที่รหัสที่มาสำหรับกรณีTypedValue.COMPLEX_UNIT_DIP, จะถูกส่งกลับที่คุณต้องการจริงvalue * metrics.density value * metrics.densityดังนั้นสิ่งที่คุณกำลัง getting` valueInDp` ไม่ได้อยู่ในdpสำหรับในvalueInPx px
bitbybit

^ typo ฉันหมายถึง"... ที่คุณต้องการจริง ๆvalue / metrics.density"
bitbybit

31

ได้อย่างมีประสิทธิภาพเลยทีเดียว

DP เป็นพิกเซล:

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

พิกเซลเป็น DP:

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

หวังว่านี่จะช่วยคุณได้


4
ดีกว่าเพราะไม่จำเป็นต้องใช้บริบท :) ขอบคุณ
Ayaz Alifov

2
คำตอบที่ดีที่สุด! น่าเสียดายคำตอบ "ทำเครื่องหมายว่าถูกต้อง" ผิด โดยเฉพาะอย่างยิ่งเนื่องจากมันใช้displayMetrics.xdpiซึ่งแตกต่างกันในอุปกรณ์ใด ๆ น่าเศร้าคำตอบที่ผิดนอกจากนี้ยังมี upvotes มากที่สุด
toom

ดี ควรทำเครื่องหมายเป็นคำตอบที่ดีที่สุด kotlin:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
Raphael C

28

เพียงแค่เรียกgetResources().getDimensionPixelSize(R.dimen.your_dimension)ให้แปลงจากdpหน่วยเป็นพิกเซล


3
ตามเอกสารนี้จะเหมือนกับ getDimension () ยกเว้นค่าที่ส่งคืนจะถูกแปลงเป็นพิกเซลจำนวนเต็มเพื่อใช้เป็นขนาด การแปลงขนาดเกี่ยวข้องกับการปัดเศษค่าฐานและทำให้แน่ใจว่าค่าฐานที่ไม่เป็นศูนย์มีขนาดอย่างน้อยหนึ่งพิกเซล
CJBS


6
px = dp * (dpi / 160)

dp = px * (160 / dpi)

แม้ว่าตอนนี้ฉันจะคิดถึงมันแล้ว ... จะมีการหารด้วยศูนย์ในสูตรดั้งเดิมของ Brams ได้อย่างไร? displayMetrics.densityDpi จะเป็น 120, 160, 240 หรือ 320, ไม่เคย 0
ct_rob

2
(displayMetrics.densityDpi / 160)- ส่วนนี้สามารถรับ 0 ได้บนอุปกรณ์ขนาดเล็กซึ่งมีการคำนวณเช่น120/160ทั้งสองค่าเป็น int ซึ่งผลลัพธ์เป็น 0 ซึ่งลงท้าย(int) (px/0)ด้วย

อ่าคุณพูดถูก ดังนั้นสิ่งที่เขาต้องทำคือใช้สูตรยาว ๆ ของเขา: 160.0
ct_rob

คำตอบนี้คล้ายกับคำสั่งด้านบนเนื่องจาก DisplayMetrics.DENSITY_DEFAULT มีค่า 160 ค่า แต่คุณกรุณาแจ้งให้เราทราบว่า DPI คืออะไรที่เราคำนวณ
John smith

3

ในกรณีส่วนใหญ่ฟังก์ชั่นการแปลงจะถูกเรียกบ่อยครั้ง เราสามารถปรับให้เหมาะสมด้วยการเพิ่มการบันทึก ดังนั้นจึงไม่คำนวณทุกครั้งที่มีการเรียกใช้ฟังก์ชัน

มาประกาศ HashMap ที่จะเก็บค่าที่คำนวณได้

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

ฟังก์ชั่นที่คำนวณค่าพิกเซล:

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

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

    }

ฟังก์ชั่นบันทึกความจำที่ส่งคืนค่าจาก HashMap และเก็บรักษาบันทึกของค่าก่อนหน้า

การบันทึกสามารถนำไปใช้ได้หลายวิธีใน Java สำหรับ 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 รองรับฟังก์ชั่น Lambda :

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

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

ขอบคุณ


นอกจากนี้ที่ดีในการแก้ปัญหาที่กำหนด
Bram

ฉันจะแปลกใจถ้าการคำนวณการแปลงไม่ได้เร็วอย่างที่การค้นหาแผนที่และไม่พูดถึงการซิงโครไนซ์ คุณมีผลการทดสอบเพื่อแสดงให้เห็นว่าการเพิ่มประสิทธิภาพนี้มีประโยชน์หรือไม่? บน Android ที่มีหน่วยความจำที่ใช้งานได้ จำกัด หน่วยความจำการซื้อขายสำหรับความพยายามคำนวณไม่ใช่สิ่งที่คุณควรทำโดยไม่มีเหตุผล
Lorne Laliberte


2

funtions ด้านล่างทำงานได้ดีสำหรับฉันในทุกอุปกรณ์

นำมาจาก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

คุณสามารถใช้[DisplayMatrics][1]และกำหนดความหนาแน่นของหน้าจอ บางสิ่งเช่นนี้

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

เพราะฉันจำได้ว่าควรใช้พื้นสำหรับออฟเซ็ตและปัดเศษเพื่อความกว้าง


1

หากคุณกำลังมองหาเครื่องคิดเลขออนไลน์สำหรับการแปลง DP, SP, นิ้วมิลลิเมตรจุดหรือพิกเซลไปและกลับจากอีกคนหนึ่งที่มีความหนาแน่นของหน้าจอที่แตกต่างกันนี้เป็นเครื่องมือที่สมบูรณ์แบบที่สุดที่ฉันรู้


1

// เพื่อรับในรูปของทศนิยม / ลอย

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

}

________________________________________________________________________________

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

ใช้ส่วนขยาย Kotlin เหล่านี้:

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

รู้สึกอิสระที่จะใช้วิธีนี้ฉันเขียนว่า:

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

0

คำตอบที่ได้รับการยอมรับข้างต้นไม่ถูกต้องสมบูรณ์ ตามข้อมูลที่ได้จากการตรวจสอบซอร์สโค้ด Android:

Resources.getDimension()และgetDimensionPixelOffset()/ getDimensionPixelSize()แตกต่างกันเฉพาะในอดีตที่กลับมาfloatในขณะที่ทั้งสองหลังกลับค่าเดียวกันปัดเศษเพื่อความintเหมาะสม สำหรับพวกเขาทั้งหมดค่าตอบแทนอยู่ในพิกเซลดิบ

ทั้งสามฟังก์ชั่น implementedy โดยการโทรResources.getValue()และการแปลงจึงได้TypedValueโดยการโทรTypedValue.complexToDimension(), TypedValue.complexToDimensionPixelOffset()และTypedValue.complexToDimensionPixelSize()ตามลำดับ

ดังนั้นหากคุณต้องการรับค่า "raw" พร้อมกับหน่วยที่ระบุในแหล่ง XML ให้เรียกResources.getValue()และใช้เมธอดของTypedValueคลาส


0

ด้วยความช่วยเหลือของคำตอบอื่น ๆ ฉันเขียนฟังก์ชั่นนี้

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

0

นี่เป็นวิธีอื่นในการใช้ส่วนขยาย 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)

และจากนั้นมันสามารถใช้งานได้จากทุกที่

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

รูปแบบของคำตอบ ct_robs ด้านบนหากคุณใช้จำนวนเต็มซึ่งไม่เพียง แต่หลีกเลี่ยงการหารด้วย 0 มันยังสร้างผลลัพธ์ที่ใช้งานได้บนอุปกรณ์ขนาดเล็ก:

ในการคำนวณจำนวนเต็มที่เกี่ยวข้องกับการหารเพื่อความแม่นยำที่ยิ่งใหญ่ที่สุดคูณก่อนที่จะหารเพื่อลดผลกระทบที่ถูกตัดทอน

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

5 * 120 = 600 / 160 = 3

แทน

5 * (120 / 160 = 0) = 0

ถ้าคุณต้องการผลปัดเศษทำเช่นนี้

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

10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.