0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NASA APIを使って簡単なアプリ作ってみた②

Last updated at Posted at 2025-07-22

今回は①の続きでメニュー画面の作成を記録する。
プロジェクトは作成済み。


NASA APIを使って簡単なアプリ作ってみた①
②NASA APIを使って簡単なアプリ作ってみた② ←こちらの記事
NASA APIを使って簡単なアプリ作ってみた③
④NASA APIを使って簡単なアプリ作ってみた④  (9月中に公開予定)

Activity作成

今回 Jetpack Composeを使用するため、Empty Activityを選択する。

コンポーネントの作成

メニューは文字とボタンをコンポーネント化する。

テキストは以下を指定できるテキストのコンポーネントを作成する。

引数として以下を受け取る。

  • text:表示する文字列
  • style:文字のスタイル
  • color:文字の色 (デフォルト黒)
MenuActivity.kt
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign

@Composable
private  fun CustomText(
    text: String,
    style: TextStyle,
    color: Color = Color.Black,
) {
    Text(
        text = text,
        color = color,
        style = style,
        textAlign = TextAlign.Center  //要素の水平中央に配置
    )
}

ボタンは画面イメージのように左に画像を配置するボタンを作成する。
今回はMaterial3のElevatedButton(影があるボタン)を使用する。その要素として画像と文字を配置する。
Spacerは画像と文字列の間隔を指定している。

引数として以下を受け取る。

  • onClick:ボタン押下後の処理
  • icon:アイコン画像のid
  • iconDesc:アイコンの説明文字列
  • label:ボタンに表示する文字列
MenuActivity.kt
import androidx.compose.material3.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Modifier
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*

@Composable
private fun CustomButton(
    onClick: () -> Unit,
    icon: Int,
    iconDesc: Int,
    label: String
) {
    ElevatedButton(
        onClick = onClick,
        modifier = Modifier
            .fillMaxWidth(0.9f) //ボタンの横幅(画面の横幅の90%)
            .height(56.dp) // ボタンの縦幅
    ) {
        Image(
            painter = painterResource(id = icon), // ボタンに配置する画像
            contentDescription = stringResource(id = iconDesc), //画像の説明
            modifier = Modifier.size(24.dp) //画像の大きさ
        )
        Spacer(modifier = Modifier.width(12.dp)) //間隔の指定
        Text(
            text = label, // ボタンに表示する文字
            color = Color.Black, //文字の色(デフォルト黒)
            textAlign = TextAlign.Center, //要素の水平中央に配置
            style = MaterialTheme.typography.bodyLarge // 文字の大きさ()
        )
    }
}
  • Modifier:Composeの修飾子でcomposableを拡張できる
    • composableのサイズ、レイアウト、動作、外見の変更
    • ラベルなどの追加
    • 入力の処理
    • 要素の操作の追加
  • painterResource:PNG などのラスター化されたアセット形式を読み込む
  • stringResource:XML リソースで静的に定義されている文字列を取得する
  • MaterialTheme:GoogleのオープンソースデザインシステムであるMaterial DesignでUIデザインをカスタムできる。

previewを表示する

MenuActivity.kt
@Preview
@Composable
private fun CustomButtonPreview() {
    UniverseApplicationTheme {
        FeatureButton(
            onClick = { },
            icon = R.drawable.mars,
            iconDesc = R.string.mars_img,
            label = stringResource(id = R.string.mars_btn)
        )
    }
}

image.png

画面遷移の定義

MenuActivity.kt
// 今日の天文台の画面に遷移
private fun navigateToAstronomyToday(context: Context) {
    context.startActivity(Intent(context, AstronomyTodayActivity::class.java))
}

// 火星の気象情報画面に遷移
private fun navigateToMarsWeather(context: Context) {
    context.startActivity(Intent(context, WeatherOnMarsActivity::class.java))
}
  • startActivity():起動したいActivityを定義する
  • intent:実行するアクションを終わらすオブジェクト
    • 明示的インテント:起動するActivityを正確に把握する(←今回はこっち)
    • 暗黙的インテント:リンクを開いたり、電話を掛けるといったアクションをシステムに伝える
  • context:アプリケーション環境に関するグローバル情報へのインターフェース(参考

これで画面に必要な部品や処理の定義が完了。

画面を作成する

MenuActivity.kt
@Composable
private fun MainMenuScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center, // 画面の中央に配置
        horizontalAlignment = Alignment.CenterHorizontally, // センター水平に配置
    ) {
        // タイトル
        CustomText(
            text = stringResource(id = R.string.title),
            style = MaterialTheme.typography.headlineSmall.copy(
                fontWeight = FontWeight.ExtraBold,
                fontSize = 30.sp
            )
        )

        Spacer(modifier = Modifier.height(32.dp))
        // 操作説明文
        CustomText(
            text = stringResource(id = R.string.ope_text),
            style = MaterialTheme.typography.bodyLarge,
            textAlign = TextAlign.Center
        )

        Spacer(modifier = Modifier.height(40.dp)) // スペース
        // 今日の天文台の画面に遷移ボタン
        CustomButton(
            onClick = { navigateToAstronomyToday(context) }, // ボタンクリック時の処理
            icon = R.drawable.universe,
            iconDesc = R.string.universe_img,
            label = stringResource(id = R.string.universes_btn)
        )

        Spacer(modifier = Modifier.height(24.dp)) // スペース
        // 火星の気象情報画面に遷移ボタン
        CustomButton(
            onClick = { navigateToMarsWeather(context) }, // ボタンクリック時の処理
            icon = R.drawable.mars,
            iconDesc = R.string.mars_img,
            label = stringResource(id = R.string.mars_btn)
        )

        Spacer(modifier = Modifier.height(32.dp)) // スペース
        // 提供文
        CustomText(
            text = stringResource(id = R.string.courtesy_text),
            style = MaterialTheme.typography.labelMedium,
            color = Color.Gray
        )
    }
}
  • LocalContext.current:Jetpack ComposeでAndroidのContextを取得できる

  • Arrangement:レイアウトの子をレイアウトの主軸方向 (水平方向と垂直方向) のように配置するために使用する

  • Alignment:要素の位置を計算するためのインターフェース。親レイアウト内のレイアウトの配置を定義するために使用する(参考)

  • .copy():headlineSmall のプロパティの一部をカスタマイズできる関数

画面表示

MenuActivity.kt
class MenuActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            UniverseApplicationTheme {
                Scaffold { innerPadding ->
                    MenuScreen(Modifier.padding(innerPadding))
                }
            }
        }
    }
}
  • setContent:Composable関数が呼び出されるアクティビティのレイアウトを定義する
  • Scaffold:画面コンポジターによって管理されている生バッファへのハンドル(参考
  • innerPadding:Scaffold が自動で計算した中身用のパディング。

コード全体
MenuActivity.kt
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.universeapplication.ui.theme.UniverseApplicationTheme

class MenuActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            UniverseApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    MainMenuScreen(Modifier.padding(innerPadding))
                }
            }
        }
    }
}

private fun navigateToAstronomyToday(context: Context) {
    context.startActivity(Intent(context, AstronomyTodayActivity::class.java))
}

private fun navigateToMarsWeather(context: Context) {
    context.startActivity(Intent(context, WeatherOnMarsActivity::class.java))
}

@Composable
private fun MainMenuScreen(modifier: Modifier = Modifier) {
    val context = LocalContext.current

    Surface(
        modifier = modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier
                .fillMaxSize()
                .padding(24.dp)
        ) {
            CustomText(
                text = stringResource(id = R.string.title),
                style = MaterialTheme.typography.headlineSmall.copy(
                    fontWeight = FontWeight.ExtraBold,
                    fontSize = 30.sp
                )
            )

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

            CustomText(
                text = stringResource(id = R.string.ope_text),
                style = MaterialTheme.typography.bodyLarge,
                textAlign = TextAlign.Center
            )

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

            CustomButton(
                onClick = { navigateToAstronomyToday(context) },
                icon = R.drawable.universe,
                iconDesc = R.string.universe_img,
                label = stringResource(id = R.string.universes_btn)
            )

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

            CustomButton(
                onClick = { navigateToMarsWeather(context) },
                icon = R.drawable.mars,
                iconDesc = R.string.mars_img,
                label = stringResource(id = R.string.mars_btn)
            )

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

            CustomText(
                text = stringResource(id = R.string.courtesy_text),
                style = MaterialTheme.typography.labelMedium,
                color = Color.Gray
            )
        }
    }
}

@Composable
private fun CustomText(
    text: String,
    style: TextStyle,
    color: Color = Color.Black,
    modifier: Modifier = Modifier,
    textAlign: TextAlign? = null
) {
    Text(
        text = text,
        color = color,
        style = style,
        textAlign = textAlign,
        modifier = modifier
    )
}

@Composable
private fun CustomButton(
    onClick: () -> Unit,
    icon: Int,
    iconDesc: Int,
    label: String
) {
    ElevatedButton(
        onClick = onClick,
        modifier = Modifier
            .fillMaxWidth(0.9f)
            .height(56.dp)
    ) {
        Image(
            painter = painterResource(id = icon),
            contentDescription = stringResource(id = iconDesc),
            modifier = Modifier.size(24.dp)
        )
        Spacer(modifier = Modifier.width(12.dp))
        Text(
            text = label,
            color = Color.Black,
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.bodyLarge
        )
    }
}

振り返り

Jetpack Composeを実装で使ってみた。今回まとめることで各関数の使い方を深く知ることが出来た。
各コンポーネントの定義範囲や配置などはまだまだ勉強が必要だと感じた。

次はAPIを使用するため、Jetpack Composeのステータスなどの使い方をまとめていく。

参考にさせていただいたサイト

https://qiita.com/ZER0NE/items/7d857480157a8a3b59e6
https://qiita.com/tkmd35/items/e6abeed6ac68cbac09ed
https://qiita.com/ryotab22/items/ddfe2b425352f557f791
https://qiita.com/Otofu_droid/items/a4c2fdc4279778c65556

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?