Android ではスクリーン上の長さを、抽象的な単位「dp」「sp」で表します。実際アンドロイドの画面サイズ解像度はメーカーによってまちまちですから、アプリを開発する側からすると何か便利な方法はないかと考えてしまいます。
ピクセル密度
Androidは様々なスクリーンサイズ(インチ)で、更に異なる画素数(ピクセル)のものが販売されていて、その1つ1つに合わせたレイアウト・デザインを作り込むのは大変です。
例えばピクセルサイズの画像の貼り付けをしたい時に問題があります。
- 端末のピクセルの画面サイズに合わせて画像を拡大縮小させないといけない
- 端末によるピクセル密度が異なることもあるので、同じピクセルサイズの画面でも物理サイズが違う場合、見え方が違う
そのために dp、sp という画面サイズ、インチ数を包括し統一的に扱うことを目的とした抽象的な単位をAndroidでは提供されているわけです。
例えば、ボタンの横幅を360dpに設定すると多くのスマホで「スクリーン横幅一杯のサイズになる」というような仕組みがあれば便利、というわけで、Googleさんが作ったのが dp とsp(フォント用)です。(実際はそれほど簡単とはいかないのではありますが)
解像度単位 dp, sp
画像やテキストサイズはデジタルの世界ではピクセルという点が基本で、点が集まって画像になったり文字になったりします。
ピクセル自体には物理的大きさはないのですが、PC画面や印刷物になるといきなり物理的な関連が出てきます。dipとdpiが似ているのでややこしいのですが…
dp (dip) |
density-independent pixel | 抽象的な単位、mdpi (160 dpi) を基準とし、 mdpi のとき 1dp = 1px となる |
sp (sip) |
scale-independent pixel | ユーザーが設定したフォントサイズに影響される |
px | pixel | ピクセル、物理的な表示用セル |
ppi | pixel per inch | 1インチ(約25.4mm)に並ぶピクセルの数 |
dpi | dots per inch | 1インチ幅のドット数、値が大きいほど1ドットの物理的な大きさが小さくなり、よりきめ細かくなる。 |
Google Ref:Supporting Multiple Screens
dpiを取得するコード
1 |
int dpi = getResources().getDisplayMetrics().densityDpi; |
尚こちらの方法はAPI30から非推奨となりました
1 2 3 |
DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); int dpi = metrics.densityDpi; |
dpを取得する
1 2 |
// Get the screen's density scale float scale = getResources().getDisplayMetrics().density; |
例えば
150dp => (int)(150 * scale)
となります
Button サイズで dp, pix の関係を見てみます。
- 1dp = 2.75 pix
- 150dp x 150dp サイズのボタン
- 200dp x 100dp サイズのボタン
- 200dp x 100dpと同じサイズを 550pix x 275pix で作成
コードはこのようになります
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//package com.example.testdpsp; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @SuppressLint("SetTextI18n") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); float scale = getResources().getDisplayMetrics().density; TextView textView1 = findViewById(R.id.text_view1); textView1.setText("dp= "+String.valueOf(scale)); Button button = findViewById(R.id.button); button.setWidth(550); button.setHeight(275); button.setText("550pix x 275pix"); } } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> <TextView android:id="@+id/text_view1" android:layout_margin="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_width="150dp" android:layout_height="150dp" android:text="150dp x 150dp" android:layout_margin="10dp" /> <Button android:layout_width="200dp" android:layout_height="100dp" android:text="200dp x 100p" android:layout_margin="10dp"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp"/> </LinearLayout> |
画面サイズ、dpi、dpの関係
dpi、dp、画面サイズの関係を表にすると
密度修飾子 | 密度 | dpi | scale | px : dp | 画面サイズ |
ldpi | 低密度 | 120 | 0.75 | 1[px] = 0.75[dp] | 240 x 320[px] |
mdpi | 中密度 | 160 | 1.0 | 1[px] = 1.0[dp] | 360 x 640[px] |
hdpi | 高密度 | 240 | 1.5 | 1[px] = 1.5[dp] | 540 x 960[px] |
xhdpi | 超高密度 | 320 | 2.0 | 1[px] = 2.0[dp] | 720 x 1280[px] |
xxhdpi | 超超高密度 | 480 | 3.0 | 1[px] = 3.0[dp] | 1080 x 1920[px] |
xxxhdpi | 超超超高密度 | 640 | 4.0 | 1[px] = 4.0[dp] | 1440 x 2560[px] |
Screenサイズはhdpiでも、4インチ480×800、3.7インチ480×854などあります。スクリーン解像度のピクセル数と dpi が比例関係にはなく、画面の物理的大きさ(インチ数)も考慮されているということです。
Ref: さまざまな画面サイズをサポートするための推奨幅ブレークポイント
ちなみに こちらの方法で Pixel や Nexus のスクリーンサイズを取得するとこのようになります。
以前は360dp x 640dpを基本として考えて例外を調整するなどで対応していたりしましたが、410dp x 730dp 以上が出てきたので対応が必要になりましたね
SPサイズはユーザー設定で変わる
SPのサイズはユーザーが設定でフォントサイズを設定でき、それによってスケーリングされます。
スマホの [設定] -> [ディスプレイ] -> [フォントサイズ]
このため、spで設定した文字は、ユーザーのフォントサイズによって変化します。
1 2 |
textView.setText("ABC"); textView.setTextSize(18); // 18sp |
レイアウトはspサイズになりますが、ユーザーがフォントサイズを変えると画面上で大きくなったり小さくなったりします。
それでは、狙った画面レイアウトができません
それを阻止するためには
1 |
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); |
のように COMPLEX_UNIT_DIP を入れ
TypedValue
を設定する必要があります。
あるいは画像として文字を入れてしまうのも手です、dpとしての縛りがありますが
ただ、ユーザーが老眼だったりすると文字が小さくて見えないという不満がでるでしょうね…その辺も考慮する必要もあります。
References:
画面互換性の概要 | Android デベロッパー
各種のピクセル密度をサポートする | Android デベロッパー
配信ダッシュボード | Android デベロッパー
Pixel density | MATERIAL DESIGN
各種の画面サイズのサポート | Android デベロッパー