【Jetpack Compose】 使い方　その１

#はじめに
久しぶりにAndroidアプリの手入れをしたらJetpackComposeの仕様がかなり変わっていたので備忘録として記事にします。
まだ正式にリリースされたわけではなく、変更があるかもしれませんのでご注意ください。
公式ドキュメントを軽くまとめたものになります。
[その２](https://qiita.com/hideto1198/items/ae664af8cc04b19876ff)←続き

#Jetpack Composeとは
去年か一昨年くらいにAppleからSwiftUIが発表されました。それのAndroid版です。
通常、Androidアプリを作成するときは、**xmlファイル**と**kotlinファイル**の２つのファイルを使って作成するかと思いますが、
Jetpack Composeを使用すると、UIの全てをコードで作成することができます。

##開発環境
- iMac 2019
- Android studio Arctic Fox | 2020.3.1
- Kotlin Jetpack compose 1.0.0-rc02(正式リリースの可能性あり)

##基本

```Kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Greeting("Android")
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    Greeting("Android")
}
```
>@Composable・・・画面構成に使用する関数全てにつけなければならない (compose=構成する、構図する等の意味があります)
>@Preview・・・Android Studio内でプレビューを表示したいときにつける

基本的には`MainActivity`内には画面構成のコードを書きません
画面の部品毎にコンポーズ可能な関数(`@Composableアノテーション`)を作成していきます
今までのJetpackComposeを使ってアプリを作成するとより直感的にデザインすることが可能です
##画面構成
JetpackComposeでは画面は三次元的に捉えることが可能です
![スクリーンショット 2021-08-05 10.05.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/d13d1ac2-837b-ba65-0d38-003f2533a205.png)

このように**Column**は縦方向、**Row**は横方向、**Box**は奥行きとなっています。

###レイアウトについて
- それぞれの指定可能な引数。 **＝以降は指定しなかった時の規定値**

```kotlin
Column(
  modifier: Modifier = Modifier,
  verticalArrangement: Arrangement.Vertical = Arrangement.Top,
  horizontalAlignment: Alignment.Horizontal = Alignment.Start,
  content: ColumnScope.() -> Unit
){}
Row(
  modifier: Modifier = Modifier,
  horizontalArrangement: Arrangement.Horizontal = Arrangement.Horizontal,
  verticalAlignment: Alignment.Vertical = Alignment.Start,
  content: rowScope.() -> Unit
){}
Box(
  modifier: Modifier = Modifier,
  contentAlignment: Alignment = Alignment.TopStart,
  propagateMinConstraints: Boolean = false,
  content: BoxScope.() -> Unit
){}

```
ArrangementとAlignmentを指定して、任意の範囲内での配置場所を決定することができます。
Boxの`propagateMinConstraints`はあまりよくわからない。英語の意味的に最小の制約を伝播するという感じ。
動き自体は、左上に固定されるというものでした
`Modifier`は後述。

これら３つを使用して部品の置く場所などを決めていきます

##Text

###Text
```kotlin
@Composable
fun ExampleText(){
  Text("Hello, JetpackCompose")
}
```
アプリの文字表示はText()を使用します。
<img width="450"src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/d3509a81-45c9-3ba2-8139-8aca7ebcaec2.png">
このように普通の文字です
少し深掘りしましょう

```kotlin
Text(
  text: String,
  modifier: Modifier = Modifier,
  color: Color = Color.Unspecified,
  fontSize: TextUnit = TextUnit.Unspecified,
  fontStyle: FontStyle? = null,
  fontFamily: FontFamily? = null,
  letterSpacing: TextUnit = TextUnit.Unspecified,
  textDecoration: TextDecoration? = null,
  textAlign: TextAlign? = null,
  lineHeight: TextUnit = TextUnit.Unspecified,
  overflow: TextOverflow = TextOverflow.Clip,
  softWrap: Boolean = true
  maxLines: Int = Int.MAX_VALUE,
  onTextLayout: (TextLayoutResult) -> Unit = {},
  style: TextStyle = LocalTextStyle.current
)
```
これだけの引数があり、これらの値をいじってテキストをデザインしていきます。
よく使いそうなものだけ紹介

```Kotlin
@Composable
fun ExampleText(){
  Text("Color", color = Color.Red, modifier = Modifier.padding(5.dp))  //色指定 rgbも可→Color(255,0,0)
  Text("fontSize", fontSize = 20.sp, modifier = Modifier.padding(5.dp))  //文字サイズ
  Text("fontStyle", fontStyle = FontStyle.Italic, modifier = Modifier.padding(5.dp))  //文字スタイル
  Text("fontFamily", fontFamily = FontFamily.Cursive, modifier = Modifier.padding(5.dp))  //書式
  Text("1:maxLines\n2:maxLines", maxLines = 1, modifier = Modifier.padding(5.dp))  //行数制限 \n以降が表示されない
}
```
`結果`
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/833b44cb-e4a8-d5aa-f508-b0c6bbad0e55.png">

##TextField

```kotlin
@Composable
fun ExampleTextField(){
    var text1 by remember { mutableStateOf("TextField") }
    var text2 by remember { mutableStateOf("OutlinedTextField") }
    //var text by remember { mutableStateOf("") } ○
    //var text by remember { mutableStateOf(TextFieldValue()) } ○
    //var text = remember { mutableStateOf("") } ○
    
    TextField(
        value = text1,
        onValueChange = { text1 = it },
        label = { Text("Label") }
    )
    OutlinedTextField(
        value = text2,
        onValueChange = { text2 = it },
        label = { Text("Label") }
    )
}
```

`結果`
![スクリーンショット 2021-08-06 9.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/05de571b-8be8-63d7-915b-9639d1c030c7.png)


初期値を空白にしたい場合は**mutableStateOf("")**か、
**mutableStateOf(TextFieldValue())**にすると初期値が空白になる。両者とも違いはありません。
**TextField**と**OutlinedTextField**は見た目の違いしかありません。
`remember`と`mutableStateOf`に関しては後述。

- 指定可能な引数

```kotlin
TextField(
    value: String,
    onValueChange: (String) -> Unit,
    label: (() -> Unit)? = null,
    modifier: Modifier = Modifier
    enabled: Boolean = true,
    readOnly: Boolean = false,
    textStyle: TextStyle = LocalTextStyle.current,
    placeholder: (() -> Unit)? = null,
    leadingIcon: (() -> Unit)? = null,
    trailingIcon: (() -> Unit)? = null,
    isError: Boolean = false,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions:KeyboardOptions = KeyboardOption.Default,
    keyboardActions: KeyboardActions = KeyboardActions(),
    singleLine: Boolean = false,
    maxLines: Int = Int.MAX_VALUE,
    interactionSource: MutableInteractionSource = ,
    shape: Shape = ,
    colors: TextFieldColors = 
```
よく使いそうなものだけ紹介

```kotlin
@Composable
fun LoginContent(){
    var mailaddress by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    val focusManger = LocalFocusManager.current          //returnキーでキーボードを閉じるときに使用
	    
    Column{
        TextField(
            value = mailaddress,
            onValueChange = { mailaddress = it},
            label = { Text("メールアドレス") },
            placeholder = { Text("example@emai.com") },
            modifier = Modifier.padding(10.dp),
            singleLine = true,
            maxLines = 1,
            keyboardOptions = KeyboardOptions(             //キーボードのタイプとチェックマークを押した時のイベント指定
                    keyboardType = KeyboardType.Email,
                    imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(onDone = {   //キーボードを閉じる操作をする
                focusManager.clearFocus()
            })
        )
        OutlinedTextField(
            value = password,
            onValueChange = { password = it},
            label = { Text("パスワード") },
            placeholder = { Text("8文字以上で英字・数字を含む文字",) },
            modifier = Modifier.padding(10.dp),
            singleLine = true,
            maxLines = 1,
            keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Password,
                    imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(onDone = {
                focusManager.clearFocus()
            }),
            visualTransformation = PasswordVisualTransformation()
        )
    }
}
```
`結果`
<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/1c1e3920-65c9-aa6e-ae2b-ed6f2a9df901.gif">

会員制のアプリとかはよくあるパスワードがアスタリスクになる
あとは、キーボードの右下にある完了ボタン？を押すとキーボードが自動で閉じるようにするのもよく使いそうですね



##その他のText
他にも面白い使い方を見つけたので紹介します

- `一つのTextで複数のスタイルを使用したい時`

```Kotlin
@Composable
fun MulitipleStylesText(){
    Text(
        buildAnnotatedString {
            withStyle(style = SpanStyle(Color.Blue)){
                append("He")
            }
            append("llo, ")
            withStyle(SpanStyle(Color.Green)){
                append("wor")
            }
            append("ld")    
        },
        modifier = Modifier.padding(3.dp)
    )
}
```

`結果`
![スクリーンショット 2021-08-05 15.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/2dd72ef9-27bb-49a7-161d-d0372f96a222.png)

- 選択可能なテキストを使用したい時

```kotlin
fun SelectableText(){
    SelectionContainer(modifier = Modifier.padding(3.dp)){
        Column{
            Text("ここからの文章は")
            Text("ユーザーが選択できる文章です")
            DisableSelection {
                Text("しかし、ここから")
                Text("ここまでの文章は選択できません")
            }
            Text("ここからまた、選択できる文章になりました")
        }
    }
}
```
`結果`
![スクリーンショット 2021-08-05 16.03.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/a15418fe-0e07-82ac-1e93-f929c543beac.png)

##Graphic
Kotlinで図形とかを作成するのがかなり手間だったが、Jetpack Composeではかなり素早く簡単にできます。

- 基本コード

```kotlin
@Composable
fun Graphics(){
    Canvas(modifier = Modifier.fillMaxSize()) {
        val canvasWidth = size.width             //キャンバスの横のサイズ取得
        val canvasHeight = size.height           //キャンバスの縦のサイズ取得
        //最初に描画される
        drawLine(
            start = Offset(x = canvasWidth, y = 0f),   //線の始点
            end = Offset(x = 0f, y = canvasHeight),    //線の終点
            color = Color.Red,                         //線の色     
            strokeWidth = 10F                          //線の太さ
        )
        //2番目に描画される
        drawCircle(
            center = Offset(x = canvasWidth/2,y = canvasHeight/2),   //円の中心
            radius = size.minDimension/6,                            //円の半径
            color = Color.Green                                      //円の色
        )
    }
}
```

>グラフィックを扱う際は`Canvas`を使用しなければならない
>CanvasはX,Y,Zの3次元。上記の場合、線の上に円が被さる

`結果`


<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/2875cbda-3a2e-ea19-7fcf-d5ce2a84485a.png">


以下は、キャンバスと他のビューを一緒に使う場合。

```kotlin
@Composable
fun MainContent(){
    Box(modifier = Modifier.fillMaxSize()) {
        LoginContent()
        Graphics()
    }
}
@Composable
fun Graphics() {
    Canvas(modifier = Modifier.fillMaxSize()) {
        val canvasWidth = size.width
        val canvasHeight = size.height

        drawLine(
            start = Offset(x = canvasWidth, y = 0f),
            end = Offset(x = 0f, y = canvasHeight),
            color = Color.Red,
            strokeWidth = 10F
        )
        drawCircle(
            center = Offset(x = canvasWidth / 2, y = canvasHeight / 2),
            radius = size.minDimension / 6,
            color = Color.Green
        )
    }
}
@Composable
fun LoginContent(){
    var mailAddress by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    Column {
        OutlinedTextField(
            value = mailAddress,
            onValueChange = { mailAddress = it },
            label = { Text("メールアドレス") },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
            modifier = Modifier.padding(3.dp)
        )
        OutlinedTextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("パスワード") },
            visualTransformation = PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
            modifier = Modifier.padding(3.dp)
        )
    }
}
```

`結果`
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/48513489-84c3-af66-a8a2-7acaa6bd62cb.gif">

> 今回は`Box`を使用しましたが、当然`Column`、`Row`も使用可能

Kotlinでこれを再現するのにどれだけ苦労するのか忘れちゃいましたが(xmlもいじらないといけなかったような)、かなり簡単に思えます。

- 四角の場合

```kotlin
@Composable
fun Graphics(){
    Canvas(modifier = Modifier.fillMaxSize()){
        val canvasWidth = size.width
        val canvasHeight = size.height
        //一番上
        drawRect(
            color = Color.Gray,                      //四角の色
            topLeft = Offset(x = 0F, y = 0F),        //四角の始点（左上）
            size = Size(width = 300F, height = 300F) //四角の大きさ
        )
        //真ん中
        rotate(degrees = 45F) { //４５度傾ける
            drawRect(
                color = Color.Blue,
                topLeft = Offset(x = canvasWidth / 2 - 150F, y = canvasHeight / 2 - 250F),
                size = Size(300F, 500F)
            )
        }
        //一番下
        drawRect(
            color = Color.Magenta,
            topLeft = Offset(x = canvasWidth - 300F, y = canvasHeight - 300F),
            size = Size(300F,300F),
            style = Stroke(width = 10F)  //四角形のスタイルを決める
        )
    }
}
```

> `rotate`を使用して、四角形の中心から傾けさせる。
> `style`を指定することで枠のみの四角形もできる。指定しないと、規定値の`styl = Fill`になる

`結果`
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/87c27278-ffb8-0131-aa9e-e232bb9e69d5.png">

やはり、より直感的にUIの作成ができます。

##Animation
アプリではかなり重要なアニメーションです。アニメーションの無いアプリはほとんどないでしょう...
今までJetpack Composeはかなり簡素化されてわかりやすいものだったかと思いますが、Animationは結構難しいです。
アニメーションを加えたい場所によって使うものが変わってきます。
**Animationの種類**

- `AnimationVisibility(試験運用版)`
- `Modifier.animateContentSize`
- `Crossfade`
- `rememberInfiniteTransition`
- `updateTransition`
- `animte*AsState`
- `Animation`
- `Animatable`
- `AnimationState`
- `animate`

の種類がありました。また、[公式ドキュメント](https://developer.android.google.cn/jetpack/compose/animation?hl=ja#high-level-apis)には以下のようなフローチャートが紹介されいます。

![スクリーンショット 2021-08-06 17.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/e4ae3bb4-a13e-6a81-a7a6-eb003269c920.png)

所々状況が想像できない箇所がありますが、これがあればなんとか使い分けれそうです。

#### `AnimatedVisibility(コンテンツの表示・非表示)`

<u>これは試験運用となっておりますので、変更または削除される可能性がありますのでご注意ください</u>

```kotlin
@ExperimentalAnimationApi
@Composable
fun Animation(){
    var isShow by remember { mutableStateOf(false) }
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
        ){
        AnimatedVisibility(
            visible = isShow,
            enter = slideInHorizontally(
                initialOffsetX = {-400}
            ),
            exit = slideOutHorizontally(
                targetOffsetX = {400}
            )
        ) {
            Text("AnimatedVisibility")
        }
        Spacer(modifier = Modifier.padding(10.dp))
        Button(
            onClick = { isShow = !isShow }
        ) {
            Text(if(isShow) "非表示" else "表示")
        }
    }
}
```

> AnimatedVisibilityには`enter`と`exit`を指定する。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/412178c3-b537-b87a-6343-aff26b4bfa2e.gif">

##### enterとexitに指定できるもの
- `EnterTransition`

|オブジェクト名|動作|引数等|
|:--|:--|:--|
|fadeIn|フェードイン|fadeIn(initialAlpha = 0.6F)|
|slideIn|任意の場所からスライドさせて表示|slideIn(initialOffset = {IntOffset(x: Int, y: Int)})
|expandIn|任意の起点から広がるように表示|expandIn(expandFrom = Alignment.BottomEnd)|
|expandHorizontally|expandInの横|expandHorizontally(expandFrom = Alignment.Start, initialWidth = { 100 })|
|expandVertically|expandInの縦バージョン|expandVertically(expandFrom = Alignment.Bottom, initialHeight = {100})|
|slideInHorizontally|slideInの横バージョン|slideInHorizontally(initialOffsetX = { -100 })|
|slideInVertically|slideInの縦バージョン|slideInVertically(initialOffsetY = { - 100 })|
- `ExitTransition`

|オブジェクト名|動作|例|
|:--|:--|:--|
|fadeOut|フェードアウト|fadeOut(initialAlpha = 0.3F)|
|slideOut|現在の場所から任意の場所へスライドさせて非表示|slideOut(targetOffset = { IntOffset(x: -100, y:100)})|
|shrinkOut|現在の場所から任意のサイズに縮ませながら非表示|shrinkOut(targetSize = { IntSize(0,0) })|
|shrinkHorizontally|shrinkOutの横バージョン|shrinkHorizontally(shrinkTowards = Alignment.End, targetWidth = {0})|
|shrinkVertically|shrinkOutの縦バージョン|shrinkVertically(shrinkTowards = Alignment.Bottom, targetHeight = {0})|
|slideOutHorizontally|slideOutの横バージョン|slideOutHorizontally(targetOffsetX = {-300})|
|slideOutVertically|slideOutの縦バージョン|slideOutVertically(targetOffsetY = {-200})|

<font color = "FF0000">※複数合わせて使いたい場合は`+`でつなげる</font>
> exit = fadeOut() + shrinkOut() + ...


<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/0b306789-e37e-d7a9-dca5-443ee31f12c0.gif">

#### `animateContentSize(コンテンツのサイズ)`

```kotlin
@Composable
fun Animation(){
    var isBig1 by remember { mutableStateOf(false) }
    var isBig2 by remember { mutableStateOf(false) }
    Column() {
        //アニメーションなし
        Text(
            text = "アニメーションなし",
            fontSize = if(isBig1) 35.sp else 20.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Blue)
                .padding(5.dp)
        )
        Button(
            onClick = { isBig1 = !isBig1 },
            modifier = Modifier.padding(5.dp)
        ) {
            Text(if(isBig1) "縮小" else "拡大")
        }
        //アニメーションあり
        Text(
            text = "アニメーションあり",
            fontSize = if(isBig2) 35.sp else 20.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Blue)
                .padding(5.dp)
                .animateContentSize()              //これを指定する。
        )
        Button(
            onClick = { isBig2 = !isBig2 },
            modifier = Modifier.padding(5.dp)
        ) {
            Text(if(isBig2) "縮小" else "拡大")
        }
    }
}
```

<img width = "250"src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/0450a678-11de-96b1-c6c6-20b93eaaaac2.gif">

#### `Crossfade(ページの切り替え等)`

```kotlin
@Composable
fun Animation(){
    var currentPage by remember { mutableStateOf("A") }
    Column {
        Text("アニメーションあり")
        Crossfade(targetState = currentPage) { screen ->
            when (screen) {
                "A" -> A()
                "B" -> B()
            }
        }
        Text("アニメーションなし")
        when(currentPage){
            "A" -> A()
            "B" -> B()
        }
        Button(onClick = { currentPage = if(currentPage == "A") "B" else "A" }) {
            Text(text = "切り替え")
        }
    }
}

@Composable
fun A(){
    Box(
        modifier = Modifier
            .height(300.dp)
            .fillMaxWidth()
        ,
        contentAlignment = Alignment.Center
    ) {
        Canvas(modifier = Modifier.fillMaxSize()){
            drawRect(
                color = Color.Red
            )
        }
        Text(
            text = "This page is A",
            color = Color.White
        )
    }
}

@Composable
fun B(){
    Box(
        modifier = Modifier
            .height(300.dp)
            .fillMaxWidth()
        ,
        contentAlignment = Alignment.Center
    ) {
        Canvas(modifier = Modifier.fillMaxSize()){
            drawRect(
                color = Color.Green
            )
        }
        Text(
            text = "This page is B",
            color = Color.White
        )
    }
}
```


<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/a029d249-b3b9-2cb7-eb99-08ab24063b48.gif">

#### `animate*AsState(簡単なアニメーション)`

```kotlin
@Composable
fun Animation(){
    var enabled by remember { mutableStateOf(true)}
    val color:Color by animateColorAsState(if(enabled) Color.Black else Color.Red)
    //animateFloatAsState
    //animateSizeAsState
    Row(
        modifier = Modifier
            .fillMaxSize()
    ){
        Text(
            text="CHANGE COLOR",
            color = color,
            fontSize = 27.sp
        )
        Button(onClick = { enabled = !enabled }) {
            Text("切り替え")
        }
    }
}
```

<img width = "250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/3ae6532e-bf9e-236f-2824-589f2446d36c.gif">

>他に、`Float`,`Dp`,`Size`,`Bounds`,`Offset`,`Rect`,`Int`,`IntOffset`,`IntSize`がある。
>汎用型を受け入れる`animateValueAsState`もある

#### `rememberInfiniteTransition(ループ)`

```kotlin
@Composable
fun Animation(){
    val infiniteTransition = rememberInfiniteTransition()
    val colors = arrayListOf<Color>()
    var delay = 0
    for(i in 0..11) {
    	val color by infiniteTransition.animateColor(
    		initialValue = Color.LightGray,
    		targetValue = Color.White,
    		animationSpec = infiniteRepeatable(
    			animation = tween(1200, delay, easing = LinearEasing),
    			repeatMode = RepeatMode.Reverse
    		)
    	)
    	colors.add(color)
    	delay += 100
    }
    val top = Offset(550F,900F)
    val bottom = Offset(550F,1100F)
    val radius:Float  = (bottom.y - top.y) / 2F
    val left = Offset(top.x - radius,top.y + radius)
    val right = Offset(top.x + radius, top.y + radius)

    val offsets:List<Offset> = arrayListOf(
        top,
        Offset(top.x+radius/2F,right.y-radius*sqrt(3F)/2F),
        Offset(top.x+radius*sqrt(3F)/2F,right.y-radius/2F),
        right,
        Offset(top.x+radius*sqrt(3F)/2F,right.y+radius/2F),
        Offset(top.x+radius/2F,right.y+radius*sqrt(3F)/2F),
        bottom,
        Offset(bottom.x-radius/2F,left.y+radius*sqrt(3F)/2F),
        Offset(bottom.x-radius*sqrt(3F)/2F,left.y+radius/2F),
        left,
        Offset(bottom.x-radius/2F,left.y-radius*sqrt(3F)/2F),
        Offset(bottom.x-radius*sqrt(3F)/2F,left.y-radius/2F),
    )
    Box(
    	modifier = Modifier
            .fillMaxSize()
    ){
        Canvas(modifier = Modifier.fillMaxSize()){
            for((index,offset) in offsets.withIndex()){
                drawCircle(
                    color = colors[index],
                    radius = 20F,
                    center = offset
                )
            }
        }
    }
}
```
だいぶ歪だけど、一応ロード画面のようなもの
<img width="230" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186268/8d84579c-bd97-5473-5234-a655c3def154.gif">


一旦ここで終了します。
まだまだドキュメントがあるので近いうちにその２を投稿したいと思います。

>参考文献
>[公式ドキュメント](https://developer.android.google.cn/jetpack/compose/documentation?hl=ja)