皆さん、こんにちは!
今日はAndroid 17で追加された超便利な新機能、EyeDropper APIについてご紹介したいと思います。皆さんはあの色、何色だろう?って思ったこと、ありませんか?

ウェブサイトのカッコいいボタンの色、お気に入りアプリのテーマカラー、画像の中の綺麗な色…今まではスクリーンショット撮って、画像編集アプリで調べて、HEXコードをメモして…めちゃくちゃ面倒でしたよね。でも、もうその時代は終わりました! 

Android 17で追加されたEyeDropper APIを使えば、画面上のどんな色でもタップ一つで取得できちゃうんです。しかも、パーミッション不要

「スクリーンショットの権限ください」とか「ストレージアクセスの権限ください」とか、あの怖いダイアログを見せる必要もありません。ユーザーフレンドリーで、開発者フレンドリーで、セキュアな夢のようなAPIです。


今回、この記事でカバーする内容としては以下になります。

– EyeDropper APIの基本的な使い方
– 実装方法を完全解説(コピペOK!)
– 実際に使ってみた感想

実際に使ってみたので、そのステップをすべて共有します!読むだけなら10分、実装まで含めて30分くらいです!それでは始めましょう!


⚠️ 重要な注意点
この記事執筆時点(2026年3月)で、Android 17はまだプレビュー版(Developer Preview)です。正式リリースは2026年Q3頃の予定です。実際のデバイスでは動作せず、エミュレーターのみで動作確認できます。


この記事で作るもの
実装を始める前に、何を作るのか見ておきましょう!

機能
1. ボタンをタップすると、システムのEyeDropperが起動
2. 画面上の好きな場所の色を選択
3. HEX、RGB、Int形式で色情報を表示
4. 選択した色をプレビュー表示

シンプルだけど、実用的なデモアプリです!これをベースに、カラーパレット管理アプリとか、テーマジェネレーターとか、色々発展させられますよ!


そもそもEyeDropper APIって何?
簡単に言うと、画面上の好きな場所の色を取得できるシステムレベルのAPIです。

デザインツールとか使ったことある人なら、スポイトツール(カラーピッカー)みたいなやつですね!PhotoshopやFigmaで「この色いいな」ってスポイトでクリックするアレです。それがAndroidのシステムレベルで使えるようになったんです!


– デザインツールアプリを作りたい
– お気に入りの画像から配色を抽出したい
– 他のアプリのUIカラーを参考にしたい
– Material 3のテーマカラーを決めたい
– アクセシビリティチェック(色のコントラスト比を調べる)


Google公式の説明によると

EyeDropper API: 新しいシステム API により、アプリは画面キャプチャの権限を必要とせずに、ディスプレイの任意の場所からピクセルの色を取得できます”

要するに、超パワフルだけど、超安全!ってことです!


昔のやり方(めちゃくちゃ大変)
スクリーンショット撮って…ストレージ権限もらって…Bitmap作って…ピクセル取得して…めんどくさい!!

– ストレージの権限が必要
– 200行以上のコード
– メモリ使いまくり
– バッテリーも食う
– セキュリティリスク


EyeDropper APIならこれだけ!


// たったこれだけ! 
val intent = Intent("android.intent.action.OPEN_EYE_DROPPER")
launcher.launch(intent)
// 色が返ってくる!
val color = result.data?.getIntExtra("android.intent.extra.COLOR", 0)

パーミッション不要
わずか15行程度
メモリ効率的
セキュア
超簡単!

ね?全然違うでしょ?


これがどれくらいすごいかというと
従来の方法だと、アプリが画面全体をキャプチャする必要がありました。つまり、ユーザーの個人情報(メッセージ、パスワード、銀行アプリの残高など)も全部見えちゃう可能性があったわけです。だからGoogleも推奨していませんでした。でも、EyeDropper APIはシステムが処理してくれるので、アプリ側には選択した色のInt値だけが返ってきます。セキュア!しかも、ユーザーが自分で選択するまで何も起きません。勝手にスクリーンショット撮られることもありません。ユーザーフレンドリー!まさに、現代のAndroidが目指す方向性そのものです


始める前の準備
必要なもの
まず、Android 17のエミュレーターが必要です!

Android 17ってまだプレビュー版じゃん!
そうなんです!2026年3月現在、Android 17はまだプレビュー版(ベータ段階)です。でも、だからこそ今のうちに試しておく価値があるんです!正式リリースされたら、あなたのアプリはいち早く最新機能を使える先進的なアプリとして注目されるかもしれません

なぜ?
このAPIはAndroid 17(コードネーム: Cinnamon Bun)からの新機能で、古いバージョンでは動きません。Android 16以下ではエラーが発生します。

準備リスト
– Android Studio Panda 2 | 2025.3.2
– Android 17 のエミュレーター(これから作ります!)
– 8GB以上のRAM(推奨、4GBでもギリギリいけます)
– 10GB以上の空きストレージ
– コーヒー(ダウンロード待ち時間用)


ステップ 1 : プロジェクトを作成
さて、コーディングを始めましょう!

1-1. build.gradle.ktsを確認
まず、プロジェクトがAndroid 17をターゲットにしているか確認します。


android {
    namespace = "com.example.forblogapplication"
    compileSdkPreview = "CinnamonBun"

    defaultConfig {
        applicationId = "com.example.forblogapplication"
        minSdk = 24
        targetSdk = 37
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

1-2. 必要な依存関係
Jetpack Composeを使うので、これらが入っているか確認

dependencies {
    implementation("androidx.activity:activity-compose:1.8.0")
    implementation("androidx.compose.material3:material3")
    // ...その他
}

ステップ 2: EyeDropper APIを実装

技術スタック
– Kotlin
– Jetpack Compose
– Material 3 Design
– EyeDropper API (Android 17+)

2-1. 定数を定義
まず、Intent用の定数を定義しましょう。

// MainActivity.kt の先頭に
private const val ACTION_OPEN_EYE_DROPPER = "android.intent.action.OPEN_EYE_DROPPER"
private const val EXTRA_COLOR = "android.intent.extra.COLOR"

2-2. 状態を管理
選択した色を保存する状態を作ります。


@Composable
fun EyeDropperDemo() {
val context = LocalContext.current

// STEP 1: State holder for picked color - default set to black
var pickedColor by remember { mutableStateOf(Color.Black) }
var currentStep by remember { mutableStateOf(0) }
var colorHistory by remember { mutableStateOf(listOf<Color>()) }

2-3. ActivityResult Launcherを設定
これが一番重要!ここでEyeDropperからの結果を受け取ります。

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        // ユーザーが色を選択したか確認
        if (result.resultCode == Activity.RESULT_OK) {
            // 選択した色を取得!
            result.data?.getIntExtra(
                EXTRA_COLOR,
                Color.Black.value.toInt()
            )?.let { colorInt ->
                Log.d("EyeDropper", "選択した色: $colorInt")
                
                // 状態を更新
                pickedColor = Color(colorInt)
                
                // 履歴に追加
                colorHistory = colorHistory + pickedColor
            }
        } else {
            Log.d("EyeDropper", "キャンセルされました")
        }
    }

ポイント解説
– `RESULT_OK` = ユーザーが色を選択した
– `EXTRA_COLOR` = 選択した色のInt値が入ってる
– `Color(colorInt)` = IntからComposeのColorオブジェクトに変換


2-4. ボタンを作る
ユーザーがタップするボタンを作りましょう!

      // Action Button
        Button(
            onClick = {
                // Check if EyeDropper is available
                if (!isEyeDropperAvailable(context)) {
                    errorMessage = if (Build.VERSION.SDK_INT < 37) {
                        "EyeDrop APIはAndroid 17 (API 37)以降で利用可能です。\n\n現在のデバイス: Android ${Build.VERSION.SDK_INT}"
                    } else {
                        "EyeDrop APIが利用できません。\nシステムがこの機能をサポートしていない可能性があります。"
                    }
                    showErrorDialog = true
                    return@Button
                }
                
                // STEP 4: Launch EyeDropper intent (Android 17+)
                try {
                    val intent = Intent(ACTION_OPEN_EYE_DROPPER)
                    launcher.launch(intent)
                    currentStep = 2
                } catch (e: ActivityNotFoundException) {
                    errorMessage = "EyeDropper機能が見つかりません。\n\nAndroid 17以降のデバイスでお試しください。"
                    showErrorDialog = true
                } catch (e: Exception) {
                    errorMessage = "エラーが発生しました: ${e.message}"
                    showErrorDialog = true
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .height(56.dp),
            colors = ButtonDefaults.buttonColors(
                containerColor = MaterialTheme.colorScheme.primary
            )
        ) {
            Text(
                text = "🎨 色を画面から選択してね",
                fontSize = 18.sp,
                fontWeight = FontWeight.SemiBold
            )
        }

2-5. 選択した色を表示
色を取得したら、それを表示しましょう!


    // 色のプレビューボックス
    Box(
        modifier = Modifier
            .size(150.dp)
            .background(pickedColor, shape = RoundedCornerShape(16.dp))
            .border(
                width = 2.dp,
                color = MaterialTheme.colorScheme.outline,
                shape = RoundedCornerShape(16.dp)
            )
    )
    
    Spacer(modifier = Modifier.height(16.dp))
    
    // 色の情報を表示
    Text("HEX: ${colorToHex(pickedColor)}")
    Text("RGB: ${colorToRgb(pickedColor)}")
    Text("Int: ${pickedColor.value.toInt()}")

2-6. ヘルパー関数
HEXとRGBに変換する関数も作っておきましょう。


// HEX形式に変換(例: #FF5733)
fun colorToHex(color: Color): String {
    val red = (color.red * 255).toInt()
    val green = (color.green * 255).toInt()
    val blue = (color.blue * 255).toInt()
    return "#%02X%02X%02X".format(red, green, blue)
}

// RGB形式に変換(例: (255, 87, 51))
fun colorToRgb(color: Color): String {
    val red = (color.red * 255).toInt()
    val green = (color.green * 255).toInt()
    val blue = (color.blue * 255).toInt()
    return "($red, $green, $blue)"
}

ステップ3: 互換性対応(重要!)
Android 17未満のデバイスでは動かないので、チェックを入れましょう。


// Android 17未満の場合は警告を表示
if (Build.VERSION.SDK_INT < 37) {
    Card(
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.errorContainer
        )
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = "⚠️ 互換性に関する注意",
                fontWeight = FontWeight.Bold,
                color = MaterialTheme.colorScheme.onErrorContainer
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "EyeDropper APIはAndroid 17以降でのみ利用可能です。\n" +
                      "現在のバージョン: Android ${Build.VERSION.RELEASE}",
                color = MaterialTheme.colorScheme.onErrorContainer
            )
        }
    }
}

Android 17未満の端末でアプリを起動した場合、エラーダイアログが表示されるようにしました。


ステップ4: 実行してみる

./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk

使ってみる

1. アプリを起動
2. 「🎨 画面から色を選択」ボタンをタップ

システムのEyeDropperが起動!
1. 画面が少し暗くなる
2. 拡大鏡が表示される
3. 指を動かすと拡大鏡が追従
4.リアルタイムでHEX値が表示される!カッコいい!
5. 好きな場所で指を離す
6. 色が取得できた!


応用アイデア

このAPIを使って、こんなアプリが作れそう!

1. デザインツール

ウェブサイトやアプリのデザインから色を抽出

カラーパレットを自動生成

HEX/RGBコードをコピー


2. テーマジェネレーター

写真から色を抽出

Material 3のテーマを自動生成

そのままアプリに適用


3. アクセシビリティチェッカー

UIの色を取得

コントラスト比を計算

アクセシビリティの問題を指摘


4. カラーマッチングアプリ

服の色を取得

似合う色を提案

コーディネートを提案


完全なコード例

package com.example.forblogapplication

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.forblogapplication.ui.theme.ForBlogApplicationTheme

// Constants for EyeDropper API (Android 17+)
private const val ACTION_OPEN_EYE_DROPPER = "android.intent.action.OPEN_EYE_DROPPER"
private const val EXTRA_COLOR = "android.intent.extra.COLOR"

// Helper function to check if EyeDropper is available
fun isEyeDropperAvailable(context: Context): Boolean {
if (Build.VERSION.SDK_INT < 37) return false
val intent = Intent(ACTION_OPEN_EYE_DROPPER)
return context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty()
}

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ForBlogApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
EyeDropperDemo()
}
}
}
}
}

@Composable
fun EyeDropperDemo() {
val context = LocalContext.current

// STEP 1: State holder for picked color - default set to black
var pickedColor by remember { mutableStateOf(Color.Black) }
var currentStep by remember { mutableStateOf(0) }
var colorHistory by remember { mutableStateOf(listOf<Color>()) }
var showErrorDialog by remember { mutableStateOf(false) }
var errorMessage by remember { mutableStateOf("") }

// STEP 2: Launcher - Initiate the color picker using ActivityResult API
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
// STEP 3: Handle the result when user picks a color
if (result.resultCode == Activity.RESULT_OK) {
result.data?.getIntExtra(
EXTRA_COLOR,
Color.Black.value.toInt()
)?.let { colorInt ->
Log.d("EyeDropperDemo", "Picked color: $colorInt")
pickedColor = Color(colorInt)
colorHistory = colorHistory + pickedColor
currentStep = 3 // Move to result step
}
} else {
Log.d("EyeDropperDemo", "Color picking cancelled")
}
}

// Error Dialog
if (showErrorDialog) {
AlertDialog(
onDismissRequest = { showErrorDialog = false },
title = {
Text(
text = "⚠️ 利用できません",
fontWeight = FontWeight.Bold
)
},
text = {
Column {
Text(errorMessage)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = "このデモを実行するには:",
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text("• Android 17以降のデバイスが必要です", fontSize = 13.sp)
Text("• 現在のAndroid Version: ${Build.VERSION.SDK_INT}", fontSize = 13.sp)
Text("• 必要な最小Version: 37 (Android 17)", fontSize = 13.sp)
Spacer(modifier = Modifier.height(12.dp))
Text(
text = "💡 ヒント: Android 17エミュレーターまたは実機でお試しください。",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 16.sp
)
}
}
,
confirmButton = {
TextButton(onClick = { showErrorDialog = false }) {
Text("了解")
}
}
)
}

Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Spacer(modifier = Modifier.height(20.dp))

// Compatibility Status Card
val isCompatible = isEyeDropperAvailable(context)
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = if (isCompatible)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.errorContainer
)
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = if (isCompatible) "✅" else "⚠️",
fontSize = 24.sp
)
Column {
Text(
text = if (isCompatible) "デバイス互換性: 利用可能" else "デバイス互換性: 利用不可",
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
color = if (isCompatible)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onErrorContainer
)
Text(
text = "Android ${Build.VERSION.SDK_INT} / 必要: API 37",
fontSize = 12.sp,
color = if (isCompatible)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onErrorContainer
)
}
}
}

Spacer(modifier = Modifier.height(24.dp))

// Header
Text(
text = "👁️ EyeDropper API デモ",
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)

Text(
text = "Android 17+ システム全体のカラーピッカー",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)

Spacer(modifier = Modifier.height(32.dp))

// Step-by-Step Guide
StepCard(
stepNumber = 1,
title = "状態の初期化",
description = "選択した色を保存するための状態ホルダーを作成",
code = "// 選択された色を保持する状態変数\nvar pickedColor by remember { mutableStateOf(Color.Black) }",
isActive = currentStep >= 0
)

Spacer(modifier = Modifier.height(16.dp))

StepCard(
stepNumber = 2,
title = "アクティビティランチャーのセットアップ",
description = "EyeDropperインテントを処理するためのアクティビティ結果ランチャーを登録",
code = """
// アクティビティ結果を受け取るランチャー
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
// 選択された色を処理
if (result.resultCode == RESULT_OK) {
val color = result.data?.getIntExtra(EXTRA_COLOR, 0)
pickedColor = Color(color)
}
}
""".trimIndent(),
isActive = currentStep >= 1
)

Spacer(modifier = Modifier.height(16.dp))

StepCard(
stepNumber = 3,
title = "EyeDropperインテントの起動",
description = "ACTION_OPEN_EYE_DROPPERインテントを作成して起動",
code = """
// EyeDropperインテントを作成
val intent = Intent(ACTION_OPEN_EYE_DROPPER)
// インテントを起動
launcher.launch(intent)
""".trimIndent(),
isActive = currentStep >= 2
)

Spacer(modifier = Modifier.height(32.dp))

// Action Button
Button(
onClick = {
// Check if EyeDropper is available
if (!isEyeDropperAvailable(context)) {
errorMessage = if (Build.VERSION.SDK_INT < 37) {
"EyeDropper APIはAndroid 17 (API 37)以降で利用可能です。\n\n現在のデバイス: Android ${Build.VERSION.SDK_INT}"
} else {
"EyeDropper APIが利用できません。\nシステムがこの機能をサポートしていない可能性があります。"
}
showErrorDialog = true
return@Button
}

// STEP 4: Launch EyeDropper intent (Android 17+)
try {
val intent = Intent(ACTION_OPEN_EYE_DROPPER)
launcher.launch(intent)
currentStep = 2
} catch (e: ActivityNotFoundException) {
errorMessage = "EyeDropper機能が見つかりません。\n\nAndroid 17以降のデバイスでお試しください。"
showErrorDialog = true
} catch (e: Exception) {
errorMessage = "エラーが発生しました: ${e.message}"
showErrorDialog = true
}
},
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
)
) {
Text(
text = "🎨 色を画面から選択してね",
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold
)
}

Spacer(modifier = Modifier.height(24.dp))

// Result Display
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "選択された色",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurfaceVariant
)

Spacer(modifier = Modifier.height(16.dp))

// Color Preview Box
Box(
modifier = Modifier
.size(150.dp)
.background(pickedColor, shape = RoundedCornerShape(16.dp))
.border(
width = 2.dp,
color = MaterialTheme.colorScheme.outline,
shape = RoundedCornerShape(16.dp)
)
)

Spacer(modifier = Modifier.height(16.dp))

// Color Info
ColorInfoRow("HEX", colorToHex(pickedColor))
ColorInfoRow("RGB", colorToRgb(pickedColor))
ColorInfoRow("Int", pickedColor.value.toInt().toString())
}
}

// Color History
if (colorHistory.isNotEmpty()) {
Spacer(modifier = Modifier.height(24.dp))

Text(
text = "カラー履歴",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.fillMaxWidth()
)

Spacer(modifier = Modifier.height(12.dp))

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
colorHistory.takeLast(5).forEach { color ->
Box(
modifier = Modifier
.size(60.dp)
.background(color, shape = RoundedCornerShape(8.dp))
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outline,
shape = RoundedCornerShape(8.dp)
)
)
}
}
}

Spacer(modifier = Modifier.height(32.dp))

// Info Section
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.tertiaryContainer
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "ℹ️ EyeDropper APIについて",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onTertiaryContainer
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "• パーミッション不要\n• システム全体のピクセル選択\n• Android 17+で利用可能\n• Intent.EXTRA_COLORとして色を返す",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onTertiaryContainer,
lineHeight = 20.sp
)
}
}
}
}

@Composable
fun StepCard(
stepNumber: Int,
title: String,
description: String,
code: String,
isActive: Boolean
) {
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = if (isActive) 4.dp else 2.dp),
colors = CardDefaults.cardColors(
containerColor = if (isActive)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Surface(
shape = RoundedCornerShape(50),
color = if (isActive)
MaterialTheme.colorScheme.primary
else
MaterialTheme.colorScheme.outline,
modifier = Modifier.size(32.dp)
) {
Box(contentAlignment = Alignment.Center) {
Text(
text = stepNumber.toString(),
color = if (isActive)
MaterialTheme.colorScheme.onPrimary
else
MaterialTheme.colorScheme.surface,
fontWeight = FontWeight.Bold
)
}
}

Text(
text = title,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = if (isActive)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onSurface
)
}

Spacer(modifier = Modifier.height(8.dp))

Text(
text = description,
fontSize = 14.sp,
color = if (isActive)
MaterialTheme.colorScheme.onPrimaryContainer
else
MaterialTheme.colorScheme.onSurfaceVariant
)

Spacer(modifier = Modifier.height(12.dp))

Surface(
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.5f),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = code,
fontSize = 12.sp,
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace,
modifier = Modifier.padding(12.dp),
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}

@Composable
fun ColorInfoRow(label: String, value: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "$label:",
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = value,
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace
)
}
}

fun colorToHex(color: Color): String {
val red = (color.red * 255).toInt()
val green = (color.green * 255).toInt()
val blue = (color.blue * 255).toInt()
return "#%02X%02X%02X".format(red, green, blue)
}

fun colorToRgb(color: Color): String {
val red = (color.red * 255).toInt()
val green = (color.green * 255).toInt()
val blue = (color.blue * 255).toInt()
return "($red, $green, $blue)"
}

@Preview(showBackground = true)
@Composable
fun EyeDropperDemoPreview() {
ForBlogApplicationTheme {
EyeDropperDemo()
}
}

実際に使ってみた感想

個人的にすごいと思ったこと 

1. めちゃくちゃ簡単
 昔は200行以上書いてたのが、15行くらいで済む!感動!
2. パーミッション不要
 ユーザーに怖い権限ダイアログを見せなくていい!これ重要!
3. システムUIがカッコいい
 Androidが用意したUIだから、統一感があってプロっぽい!
4. 精度が高い
 ピクセル単位で正確に取得できる!拡大鏡のおかげで選びやすい!
5. 速い 
 レスポンスが超速い!スクリーンショット撮って処理して…みたいな待ち時間ゼロ!


おわりに

今回は、Android 17で追加された新機能「EyeDropper API」の実装方法を解説してみました。
興味のある方は、ぜひお試し下さい!



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる