Android(kotlin) - JetPack Compose - Button

하동혁 ·2023년 8월 21일
1

Android Jetpack Compose

목록 보기
10/30
post-thumbnail

UI Button

버튼의 속성과 기능들이 매우 다양하기 때문에 실습을 통해 직접 하나씩 변경해 가며 공부했습니다.

import android.content.Context
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.dong.study.ui.theme.MyComposeStudyTheme

class ButtonsActivity : ComponentActivity() {
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyComposeStudyTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background,
                ) {
                    ButtonsContainer(this@ButtonsActivity)
                }
            }
        }
    }
}

// onClick: () -> Unit,
// modifier: Modifier = Modifier,
// enabled: Boolean = true, (클릭 여부 처리)
// interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, (사용자의 인터렉션 처리)
// elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), (그림자 적용)
// shape: Shape = FilledButtonTokens.ContainerShape, (모양)
// border: BorderStroke? = null,  (테두리)
// colors: ButtonColors = ButtonDefaults.buttonColors(),
// contentPadding: PaddingValues = ButtonDefaults.ContentPadding, (내용을 밀어넣는 공간)
// content: @Composable RowScope.() -> Unit

@Composable
@RequiresApi(Build.VERSION_CODES.O)
fun ButtonsContainer(context: Context) {
    // 버튼 테두리를 그라이데션 색상을 적용하기 위한 값
    val buttonBorderGradient = Brush.horizontalGradient(listOf(Color.Green, Color.Red))

    // 상태 감지
    val interactionSource = remember {
        MutableInteractionSource()
    }

    // 버튼이 눌러진 상태를 isPressed에 저장
    // interactionSource 변수를 원하는 버튼에 지정하면 그 버튼의 상태를 isPressed에 저장하는 것임
    val isPressed by interactionSource.collectIsPressedAsState()

    val pressState = if (isPressed) {
        "버튼 누르고 있는 중"
    } else {
        "버튼 누르는 것 stop"
    }

    val interactionSourceForSecondBtn = remember { MutableInteractionSource() }
    val isPressedForSecondBtn by interactionSourceForSecondBtn.collectIsPressedAsState()

    val pressedBtnRadius = if (isPressedForSecondBtn) 0.dp else 20.dp

    val pressedBtnRadiusWithAnim: Dp by animateDpAsState(
        if (isPressedForSecondBtn) 0.dp else 20.dp,
    )

    Column(
        modifier = Modifier
            .background(Color.White)
            .fillMaxSize(),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        // elevation 적용
        Button(
            elevation = ButtonDefaults.buttonElevation(
                defaultElevation = 10.dp,
            ),
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button elevation")
        }

        // 버튼 클릭 x
        Button(
            enabled = false,
            onClick = {
            },
        ) {
            Text(text = "Button enabled false")
        }

        // PressedElevation 적용
        // 누르면 그림자가 사라지면서 눌렀다는 변화가 발생
        Button(
            elevation = ButtonDefaults.buttonElevation(
                defaultElevation = 10.dp, // 그림자 주기
                pressedElevation = 0.dp, // 눌렀을때 그림자 없애기
            ),
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button PressedElevation")
        }

        // shape 적용
        Button(
            elevation = ButtonDefaults.buttonElevation(
                defaultElevation = 10.dp,
            ),
            shape = RoundedCornerShape(10.dp),
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button shape")
        }

        // border 적용
        Button(
            elevation = ButtonDefaults.buttonElevation(
                defaultElevation = 10.dp,
            ),
            border = BorderStroke(4.dp, buttonBorderGradient), // 굵기 , 색상 (그라데이션 색상 적용)
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button border")
        }

        // 버튼 색 적용
        Button(
            elevation = ButtonDefaults.buttonElevation(
                defaultElevation = 10.dp,
            ),
            colors = ButtonDefaults.buttonColors(
                containerColor = Color.Blue,
//                ,disabledContainerColor = Color.Red // 버튼이 비활성시 색 지정
            ),
            border = BorderStroke(4.dp, buttonBorderGradient), // 굵기 , 색상 (그라데이션 색상 적용)
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button color")
        }

        // content padding 적용
        Button(
            // 버튼 내부에 padding을 주는 것 (text와 테두리의 간격)
//            contentPadding = PaddingValues(40.dp)
            contentPadding = PaddingValues(horizontal = 40.dp),
            onClick = {
                Toast.makeText(context, "Button클릭", Toast.LENGTH_SHORT).show()
            },
        ) {
            Text(text = "Button padding")
        }

        // button 인터렉션
        Button(
            // 위에서 선언한 interactionSource 적용
            // 버튼이 눌려지면 그 상태를 해당 변수에 저장
            interactionSource = interactionSource,
            onClick = {
            },
        ) {
            Text(text = "Button 인터렉션")
        }

        Text(text = "$pressState")

        // 버튼 커스텀 그림자
        // ex) 색이 있는 그라데이션, 그림자를 넣을 위치 크기 등등
        Button(
            enabled = true,
            shape = RoundedCornerShape(10.dp),
            border = BorderStroke(4.dp, buttonBorderGradient),
            colors = ButtonDefaults.buttonColors(
                containerColor = Color.Black,
                disabledContainerColor = Color.LightGray,
            ),
            interactionSource = interactionSourceForSecondBtn,
            modifier = Modifier.drawColoredShadow( // Extensions에 fun Modifier.drawColoredShadow 적용
                color = Color.Blue,
                alpha = 0.5f, // 투명도
                borderRadius = 10.dp,
                shadowRadius = pressedBtnRadiusWithAnim,
                offsetY = 0.dp,
                offsetX = 0.dp,
            ),
            onClick = {
                Log.d("TAG", "ButtonsContainer: 버튼 5 클릭")
            },
        ) {
            Text(text = "커스텀 그라데이션", color = Color.White)
        }

//        color: Color,
//        alpha: Float = 0.2f,
//        borderRadius: Dp = 0.dp,
//        shadowRadius: Dp = 20.dp,
//        offsetY: Dp = 0.dp,
//        offsetX: Dp = 0.dp
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview4() {
    MyComposeStudyTheme {
//        ButtonsContainer()
    }
}

Extensions.kt

Extensions.kt 파일을 생성하고 아래 코드를 작성합니다.

이 kt파일은 마지막 커스텀 그라데이션 버튼에 적용하기 위한 파일입니다.

import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

@RequiresApi(Build.VERSION_CODES.O)
fun Modifier.drawColoredShadow(
    color: Color,
    alpha: Float = 0.2f,
    borderRadius: Dp = 0.dp,
    shadowRadius: Dp = 20.dp,
    offsetY: Dp = 0.dp,
    offsetX: Dp = 0.dp
) = this.drawBehind {
    val transparentColor = android.graphics.Color.toArgb(color.copy(alpha = 0.0f).value.toLong())
    val shadowColor = android.graphics.Color.toArgb(color.copy(alpha = alpha).value.toLong())
    this.drawIntoCanvas {
        val paint = Paint()
        val frameworkPaint = paint.asFrameworkPaint()
        frameworkPaint.color = transparentColor
        frameworkPaint.setShadowLayer(
            shadowRadius.toPx(),
            offsetX.toPx(),
            offsetY.toPx(),
            shadowColor
        )
        it.drawRoundRect(
            0f,
            0f,
            this.size.width,
            this.size.height,
            borderRadius.toPx(),
            borderRadius.toPx(),
            paint
        )
    }
}
  • 버튼 색상은 modifier에서 변경 불가
  • 버튼 색상은 ButtonDefaults.buttonColors( )에서 변경 가능
  • 나머지는 modifier로 배경색 변경 가능!!


Modifier (버튼)

    Button(
        onClick = {},
        modifier = Modifier.size(200.dp).padding(30.dp)
    ) {
        Icon(
            imageVector = Icons.Filled.Send,
            contentDescription = null
        )
        Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))  // 아이콘과 버튼 사이 여백 만들기 (dp로 직접 간격조절 가능)
        Text(text = "Send")
    }


Modifier.size(200.dp).padding(30.dp)을 적용하면
그림과 같이 버튼 밖깥 부분(흰색 포함)이 200.dp로 크기가 설정된다.
padding(30.dp)를 적용해서 밖깥 부분(흰색 포함) 기준으로 30.dp만큼의 padding이 적용된다.


Modifier (버튼 내부 텍스트에 적용)

  • 버튼 내부 텍스트에만 클릭이벤트가 가능하도록 처리
  • 버튼을 enabled = false
  • text에 modifier = Modifier.clickable { } 추가
    Button(
    onClick = {},
    enabled = false,
    modifier = Modifier.size(100.dp).padding(10.dp)
    ) {
    Icon(
    imageVector = Icons.Filled.Send,
    contentDescription = null
    )
    Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) // 아이콘과 버튼 사이 여백 만들기 (dp로 직접 간격조절 가능)
    Text(
    text = "Send",
    modifier = Modifier.clickable { }
    )
    }

0개의 댓글