Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack Composeで感じたしあわせ〜♡なところ

subroh_0508
November 27, 2020

Jetpack Composeで感じたしあわせ〜♡なところ

YUMEMI.apk #2 〜ようこそ!Android 11〜のLT資料です

https://yumemi.connpass.com/event/191284/

subroh_0508

November 27, 2020
Tweet

More Decks by subroh_0508

Other Decks in Technology

Transcript

  1. গ͚ͩ͠ ࡞ͬͨϞϊએ఻ ˑ$0-03.!45&3 ΞΠυϧϚελʔγϦʔζʹొ৔͢ΔΞΠυϧͷʜ w ΠϝʔδΧϥʔݕࡧ ϓϨϏϡʔ w ϞόΠϧ୺຤ΛϖϯϥΠτԽɹͰ͖ΔΞϓϦ 8FC൛63-IUUQTJNBTDPMPSNBTUFSXFCBQQ

    (JU)VCTVCSPIDPMPSNBTUFS 8FCΞϓϦ൛ϦϦʔεࡁʂ 㾎,PUMJO+4Ͱ࣮૷ ,PUMJO཰  㾎μʔΫςʔϚରԠ 㾎ଟݴޠରԠ "OESPJE൛Λ+FUQBDL$PNQPTFͰ࣮૷தʂ
  2. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎6*ίϯϙʔωϯτΛখཻ͍͞౓Ͱѻ͑Δʂ  ཻ౓͕େ͖͍ ը໘୯Ґ ɺখ͞ͳมߋ͕શମʹӨڹΛ༩͕͑ͪ  ཻ౓͕খ͍͞ ෦඼୯Ґ

    ɺมߋ͕ίϯϙʔωϯτ಺ʹऩ·Δ $IJQTͱ$IFDLCPYೖΕସ͍͑ͨ ˣ &EJU5FYU$IJQT$IFDLCPYͷ BQQMBZPVU@DPOTUSBJOU˓˓Λશͯमਖ਼ &EJU5FYU͸मਖ਼ͱؔ܎ͳ͍ͷʹʜɹɹ
  3. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎6*ίϯϙʔωϯτΛখཻ͍͞౓Ͱѻ͑Δʂ  ཻ౓͕େ͖͍ ը໘୯Ґ ɺখ͞ͳมߋ͕શମʹӨڹΛ༩͕͑ͪ  ཻ౓͕খ͍͞ ෦඼୯Ґ

    ɺมߋ͕ίϯϙʔωϯτ಺ʹऩ·Δ @Composable fun SearchBox( params: SearchParams, onParamsChange: (SearchParams) -> Unit = {}, modifier: Modifier = Modifier, ) { Column(Modifier.fillMaxWidth() + modifier) { Spacer(Modifier.preferredHeight(16.dp)) BrandChips(params.brands, ...) Spacer(Modifier.preferredHeight(16.dp)) TypeChips(params.brands, params.types, ... ) } }
  4. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎6*ίϯϙʔωϯτΛখཻ͍͞౓Ͱѻ͑Δʂ  ཻ౓͕େ͖͍ ը໘୯Ґ ɺখ͞ͳมߋ͕શମʹӨڹΛ༩͕͑ͪ  ཻ౓͕খ͍͞ ෦඼୯Ґ

    ɺมߋ͕ίϯϙʔωϯτ಺ʹऩ·Δ @Composable fun SearchBox( params: SearchParams, onParamsChange: (SearchParams) -> Unit = {}, modifier: Modifier = Modifier, ) { Column(Modifier.fillMaxWidth() + modifier) { Spacer(Modifier.preferredHeight(16.dp)) BrandChips(params.brands, ...) Spacer(Modifier.preferredHeight(16.dp)) TypeChips(params.brands, params.types, ... ) } } $IJQTͱ$IFDLCPYΛίϐϖͰೖΕସ͑ &EJU5FYUʹҰ੾৮ΕͣมߋͰ͖Δʂɹɹ
  5. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎7JFX.PEFMɾ$PSPVUJOFTͷαϙʔτ͕ॆ࣮ʂ  $PSPVUJOFT val uiModel by viewModel.uiModel.collectAsState(initial =

    ...) ɹ ColorLists( uiModel.items, onSelect = viewModel::select, onClick = { ... }, onPreviewClick = { ... }, onPenlightClick = { ... }, onAllClick = viewModel::selectAll, modifier = Modifier.fillMaxSize(), ) ྫΠϝʔδΧϥʔͷϦετදࣔ
  6. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎7JFX.PEFMɾ$PSPVUJOFTͷαϙʔτ͕ॆ࣮ʂ  $PSPVUJOFT val uiModel by viewModel.uiModel.collectAsState(initial =

    ...) ɹ ColorLists( uiModel.items, onSelect = viewModel::select, onClick = { ... }, onPreviewClick = { ... }, onPenlightClick = { ... }, onAllClick = viewModel::selectAll, modifier = Modifier.fillMaxSize(), ) ྫΠϝʔδΧϥʔͷϦετදࣔ 'MPXˠ4UBUFม׵
  7. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎7JFX.PEFMɾ$PSPVUJOFTͷαϙʔτ͕ॆ࣮ʂ  $PSPVUJOFT val uiModel by viewModel.uiModel.collectAsState(initial =

    ...) ɹ ColorLists( uiModel.items, onSelect = viewModel::select, onClick = { ... }, onPreviewClick = { ... }, onPenlightClick = { ... }, onAllClick = viewModel::selectAll, modifier = Modifier.fillMaxSize(), ) ྫΠϝʔδΧϥʔͷϦετදࣔ ϦετʹมԽ͕͋ͬͨΒ ࣗಈߋ৽ʂ
  8. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎4UBUF؅ཧͷ"1*͕࢖͍΍͍͢ʂ  ྫ%SBXFS-BZPVUPO val drawer = findViewById<DrawerLayout>(R.id.drawerLayout) //

    ։ดॲཧ drawer.closeDrawer(Gravity.LEFT) / drawer.openDrawer(Gravity.RIGHT) // ։ดঢ়ଶͷऔಘ drawer.isDrawerOpen(Gravity.LEFT) ։ดϝιου ঢ়ଶΛ%SBXFS-BZPVUͷΠϯελϯε͕࣋ͭ ˠ7JFXͱ4UBUF͕ີ݁߹ͷঢ়ଶ
  9. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎4UBUF؅ཧͷ"1*͕࢖͍΍͍͢ʂ  ྫ%SBXFS-BZPVUPO val modalDrawerState = rememberDrawerState(DrawerValue.Closed) ModalDrawerLayout(

    drawerState = modalDrawerState, drawerContent = ..., bodyContent = { ... }, ) // ։ดॲཧ modalDrawerState.open() / modalDrawerState.close() // ։ดঢ়ଶͷऔಘ modalDrawerState.isOpen / modalDrawerState.isClosed
  10. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎4UBUF؅ཧͷ"1*͕࢖͍΍͍͢ʂ  ྫ%SBXFS-BZPVUPO val modalDrawerState = rememberDrawerState(DrawerValue.Closed) ModalDrawerLayout(

    drawerState = modalDrawerState, drawerContent = ..., bodyContent = { ... }, ) // ։ดॲཧ modalDrawerState.open() / modalDrawerState.close() // ։ดঢ়ଶͷऔಘ modalDrawerState.isOpen / modalDrawerState.isClosed ։ดϝιου ঢ়ଶΛ%SBXFS4UBUFΫϥε͕࣋ͭ ˠ7JFXͱ4UBUF͕ૄ݁߹ͳঢ়ଶ
  11. ͋͠Θͤʹͳͬͨͱ͜Ζ  㾎3FBDUͱͷ਌࿨ੑ͕ߴ͍ʂ  +FUQBDL$PNQPTF΋3FBDU΋ಉ͡એݴత6*ͷࢥ૝  4UBUFGVMͳ6*ίϯϙʔωϯτͷ࣮૷ʹ༗༻ͳ"1*͕ଘࡏ  4UBUFΫϥεNVUBCMF4UBUF0Gϝιου 

    VTF4UBUFϝιου ࣅ௨ͬͨಛ௃Λ࣋ͭͨΊɺ֓೦తͳ෦෼ͷॻ͖ຯ͕ࣅ͍ͯΔ ˠ8FCΞϓϦઌߦϦϦʔεޙͷ"OESPJE࣮૷͕໨ʹݟ͑ͯૣ͔ͬͨʂ ˞ݸਓͷײ૝Ͱ͢
  12. 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  ڞ௨෦෼ΛFYQFDUम০ࢠΛ͚࣮ͭͯ૷ Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ expect open class ViewModel constructor(coroutineScope: CoroutineScope?

    = null) { protected val viewModelScope: CoroutineScope } DPMPSNBTUFSTIBSFEVUJMJUJFTTSDDPNNPO.BJO FYQFDUम০ࢠΛ͚ͭɺ7JFX.PEFMΫϥεͷଘࡏΛఆٛ͢ΔΠϝʔδ "OESPJE+4J04ͦΕͧΕʹ޲͚࣮ͨࡍͷ࣮૷͸ผͰఆٛ͢Δ
  13. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  "OESPJE޲͚ͷ࣮૷ DPMPSNBTUFSTIBSFEVUJMJUJFTTSDBOESPJE.BJO import androidx.lifecycle.viewModelScope as androidViewModelScope import

    androidx.lifecycle.ViewModel as AndroidViewModel actual open class ViewModel actual constructor( coroutineScope: CoroutineScope? ) : AndroidViewModel() { protected actual val viewModelScope = coroutineScope ?: androidViewModelScope }
  14. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  "OESPJE޲͚ͷ࣮૷ DPMPSNBTUFSTIBSFEVUJMJUJFTTSDBOESPJE.BJO import androidx.lifecycle.viewModelScope as androidViewModelScope import

    androidx.lifecycle.ViewModel as AndroidViewModel actual open class ViewModel actual constructor( coroutineScope: CoroutineScope? ) : AndroidViewModel() { protected actual val viewModelScope = coroutineScope ?: androidViewModelScope } "OESPJE޲͚࣮૷͸"OESPJE7JFX.PEFMΛܧঝ
  15. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  "OESPJE޲͚ͷ࣮૷ DPMPSNBTUFSTIBSFEVUJMJUJFTTSDBOESPJE.BJO import androidx.lifecycle.viewModelScope as androidViewModelScope import

    androidx.lifecycle.ViewModel as AndroidViewModel actual open class ViewModel actual constructor( coroutineScope: CoroutineScope? ) : AndroidViewModel() { protected actual val viewModelScope = coroutineScope ?: androidViewModelScope } "OESPJE޲͚࣮૷͸BOESPJE7JFX.PEFM4DPQFΛอ࣋
  16. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  +4޲͚ͷ࣮૷ DPMPSNBTUFSTIBSFEVUJMJUJFTTSDKT.BJO actual open class ViewModel actual

    constructor(coroutineScope: CoroutineScope?) { protected actual val viewModelScope = coroutineScope ?: MainScope() }
  17. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMΛ,PUMJO.11ʹରԠͨ͠ܗࣜͰ࣮૷  +4޲͚ͷ࣮૷ DPMPSNBTUFSTIBSFEVUJMJUJFTTSDKT.BJO actual open class ViewModel actual

    constructor(coroutineScope: CoroutineScope?) { protected actual val viewModelScope = coroutineScope ?: MainScope() } +4޲͚࣮૷͸Կ΋ܧঝ͠ͳ͍ɺ.BJO4DPQF Λอ࣋͢Δ ˠ6*ϩδοΫΛهड़͢ΔΫϥεͰϓϥοτϑΥʔϜຖͷ࣮૷ࠩΛٵऩͰ͖ͨʂ
  18. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎6*ϩδοΫΛ࣮૷  ύʔιφϧΧϥʔΛ"1*͔Βऔಘ͢Δ1SFWJFX7JFX.PEFMΫϥε class PreviewViewModel( private val repository: IdolColorsRepository,

    coroutineScope: CoroutineScope? = null, ) : ViewModel(coroutineScope) { @ExperimentalCoroutinesApi private val _idolsLoadState: MutableStateFlow<LoadState> by lazy { MutableStateFlow(LoadState.Loaded<List<IdolColor>>(listOf())) } @ExperimentalCoroutinesApi val uiModel: Flow<FullscreenPreviewUiModel> get() = _idolsLoadState.map { FullscreenPreviewUiModel(it) } .apply { launchIn(viewModelScope) } fun fetch(ids: List<String>) { val job = viewModelScope.launch(start = CoroutineStart.LAZY) { runCatching { repository.search(ids) } .onSuccess { _idolsLoadState.value = LoadState.Loaded(it) } .onFailure { _idolsLoadState.value = LoadState.Error(it) } } _idolsLoadState.value = LoadState.Loading job.start() } } ࣗલ࣮૷ͨ͠7JFX.PEFMΛܧঝˠ"OESPJEͱ+4Ͱڞ௨ར༻͕Մೳʂ
  19. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class PreviewActivity : AppCompatActivity() { private

    val viewModel: PreviewViewModel by viewModel() @ExperimentalMaterialApi @ExperimentalLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Preview(intent.screenType, viewModel, ::finish) } } }
  20. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class PreviewActivity : AppCompatActivity() { private

    val viewModel: PreviewViewModel by viewModel() @ExperimentalMaterialApi @ExperimentalLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Preview(intent.screenType, viewModel, ::finish) } } } 1SFWJFX7JFX.PEFMͷΠϯελϯεΛ"DUJWJUZʹ஫ೖ
  21. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class PreviewActivity : AppCompatActivity() { private

    val viewModel: PreviewViewModel by viewModel() @ExperimentalMaterialApi @ExperimentalLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Preview(intent.screenType, viewModel, ::finish) } } } !$PNQPTBCMFʹ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ౉͢
  22. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class FullscreenPreviewContainer : RComponent<FullscreenPreviewProps, FullscreenPreviewState>() {

    val viewModel: PreviewViewModel by inject() override fun FullscreenPreviewState.init() { uiModel = FullscreenPreviewUiModel.INITIALIZED } override fun componentDidMount() { launch { viewModel.uiModel.collect { setState { uiModel = it } } } } }
  23. class FullscreenPreviewContainer : RComponent<FullscreenPreviewProps, FullscreenPreviewState>() { val viewModel: PreviewViewModel by

    inject() override fun FullscreenPreviewState.init() { uiModel = FullscreenPreviewUiModel.INITIALIZED } override fun componentDidMount() { launch { viewModel.uiModel.collect { setState { uiModel = it } } } } } Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO 3$PNQPOFOUˠ"OESPJEͰݴ͏"DUJWJUZతͳΫϥε
  24. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class FullscreenPreviewContainer : RComponent<FullscreenPreviewProps, FullscreenPreviewState>() {

    val viewModel: PreviewViewModel by inject() override fun FullscreenPreviewState.init() { uiModel = FullscreenPreviewUiModel.INITIALIZED } override fun componentDidMount() { launch { viewModel.uiModel.collect { setState { uiModel = it } } } } } 1SFWJFX7JFX.PEFMͷΠϯελϯεΛ3$PNQPOFOUʹ஫ೖ
  25. Ͳ͏΍ͬͯڞ௨Խͤͨ͞ͷ͔ 㾎7JFX.PEFMͷJOKFDU  ,PJOΛ࢖ͬͯ1SFWJFX7JFX.PEFMͷΠϯελϯεΛ஫ೖPO class FullscreenPreviewContainer : RComponent<FullscreenPreviewProps, FullscreenPreviewState>() {

    val viewModel: PreviewViewModel by inject() override fun FullscreenPreviewState.init() { uiModel = FullscreenPreviewUiModel.INITIALIZED } override fun componentDidMount() { launch { viewModel.uiModel.collect { setState { uiModel = it } } } } } VJ.PEFM͕ߋ৽͞ΕΔ౓ʹ࠶ඳը͕͔͔ΔΑ͏ઃఆ DPNQPOFOU%JE.PVOUˠ"OESPJEͷPO$SFBUFʹ૬౰
  26. ͭΒ͍ؾ࣋ͪʹͳͬͨͱ͜Ζ  㾎.PEJpFSʹ׳Ε͕ඞཁ  7JFXͷݟͨ໨Λมߋ͢ΔϏϧμʔΫϥε  ϝιουͷ࣮ߦॱʹΑͬͯNBSHJOͷద༻Ґஔ͕มΘͬͨΓ͢Δ Modifier.preferredHeight(32.dp) .background( color

    = MaterialTheme.colors.primary, shape = CircleShape, ) .border(BorderStroke(1.dp, MaterialTheme.colors.primary), CircleShape) .wrapContentHeight(Alignment.CenterVertically) .padding(horizontal = 12.dp) όάʁͦΕͱ΋࢓༷ʁ +FUQBDL$PNQPTFɺָ͍͠ͷ͚ͩͲຊ൪ར༻͸ͪΐͬͱͭΒ͍ʜ
  27. 㾎+FUQBDL$PNQPTF Ћ൛   ཉ͍͠ػೳ͸͔ͳΓἧ͖ͬͯͨʂຊ൪Ͱ࢖͑Δ೔΋͍ۙʜʁ  7JFXͱ4UBUFͷ෼཭͕ߟྀ͞Εͨίϯϙʔωϯτɺ࢖͍΍͍͢ʂ 㾎+FUQBDL$PNQPTF 3FBDU ,PUMJO.11

     એݴత6*Ͱἧ͑ͨը໘࣮૷ ,PUMJO.11͸ඇৗʹ૬ੑ͕Αͦ͞͏  6*Ҏ֎શͯͷϩδοΫΛ,PUMJOͰ࣮૷Ͱ͖Δੈք͕΍ͬͯ͘Δʂʜ͔΋ ·ͱΊ %SBXFS-BZPVU΍#BDLESPQͷ࣮૷͕ຊ౰ʹ؆୯ʂ +FUQBDL$PNQPTFͰͷ6*࣮૷ͷָ͠͞ΛΈͳ͞Μ΋ຯΘͬͯΈͯ͸ʜʂ 5IBOLZPVGPSMJTUFOJOH