Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Navigation Component 実践編
Search
yoppie
December 03, 2019
Technology
300
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Navigation Component 実践編
yoppie
December 03, 2019
More Decks by yoppie
See All by yoppie
AmperとFleetを使ったAndroidアプリ
yoppie
0
1.1k
Androidエンジニアが Flutterでアプリを リリースするまでの苦悩
yoppie
0
550
FragmentContainerViewとは
yoppie
0
810
Recent tips collection
yoppie
0
1.1k
Distribute Android app updates with bitrise
yoppie
0
1.4k
EC2 using Lumen and DynamoDB and SNS
yoppie
1
290
sabotage
yoppie
1
990
Play with Kotlin
yoppie
1
650
Other Decks in Technology
See All in Technology
「待ち時間」の消滅と「自我消耗」の加速:生成AI時代のエンジニアを救うメンタル・リソース管理
poropinai1966
0
330
コンポーネント名には何を含めるべきなのか? / what-should-be-included-in-component-names
airrnot1106
0
190
1台から試せる!Edge IoTを使った位置情報の活用設計【SORACOM Discovery 2026】
soracom
PRO
0
110
データベース研修【MIXI 26新卒技術研修】
mixi_engineers
PRO
1
690
NetBoxを利用した作業効率化の試み_NetDevNight4
tnoha
0
380
DevOps Agentで運用判断をチーム資産にする~Agent InstructionsとAgent Skillを継続的に育てる~
fujioka6789
0
170
ここは地獄!つらい朝会を体験することで、チームとしてのより良い振る舞いに気づくワークショップ / The stand-up meeting from hell in the game industry
scrummasudar
0
440
ソフトウェアアーキテクチャ研修【MIXI 26新卒技術研修】
mixi_engineers
PRO
2
1k
エンタープライズデータへ安全につなぐ Production-ready なエージェント設計 ― AI × MCP リファレンスアーキテクチャ ― #AIDevDay
cdataj
1
330
Webの技術とガジェットで子どもも大人も楽しめるワクワク体験を提供する / Qiita Tech Festa Day 2026
you
PRO
1
310
オートマトンと字句解析でRoslynを読む
tomokusaba
0
120
人依存からAIネイティブの体制へ:バックエンド開発の裏側【SORACOM Discovery 2026】
soracom
PRO
0
130
Featured
See All Featured
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
430
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
440
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
260
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
600
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
New Earth Scene 8
popppiees
3
2.4k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
560
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
580
How GitHub (no longer) Works
holman
316
150k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
230
Transcript
/BWJHBUJPO$PNQPOFOU ࣮ફฤ
'BDFCPPLIJLPXBJJEF5XJUUFS!ZPQQJF@Y (JUIVCZPTIJZB92JJUBZPQQJF@Y ϒϩάIUUQTZPQQJFYIBUFOBCMPHDPN ZPQQJF
ͪΐͬͱখ
None
%SPJE,BJHJ
'SBHNFOU$POUBJOFS7JFX ͱ
/BWJHBUJPO$PNQPOFOU ࣮ફฤ
w "OESPJE+FUQBDLʹؚ·Ε͍ͯΔίϯϙʔωϯτ w "DUJWJUZ'SBHNFOUؒͷը໘ભҠΛγϯϓϧʹ࣮Ͱ͖Δ w ར w 'SBHNFOU5SBOTBDUJPOͷॲཧΛϥΠϒϥϦ͕ߦ͏ w %FFQ-JOLͷ࣮ͷ؆қԽ
w 4BGF"SHTΛ༻͍ͨܕ҆શͷ͠ w ը໘ભҠͷύλʔϯΛ9.-ʹఆٛ͠ՄࢹԽ /BWJHBUJPO(SBQI /BWJHBUJPO$PNQPOFOU
None
4IBSFE7JFX.PEFM Λ༻͍ͨڞ༗
՝ 'SBHNFOU͔Β'SBHNFOU͠Λ͢Δ߹ ௨ৗ4BGF"SHTΛ༻͍ͨܕ҆શͷ͢͠Δ ͔͠͠ϗετ͍ͯ͠Δ"DUJWJUZͱ֤'SBHNFOUͰԿ͔Λ ڞ༗͢Δ͜ͱ͕Ͱ͖ͳ͍ 5PPMCBSͷ5JUMFͷมߋɺ4OBDLCBSͷදࣔΛ ֤'SBHNFOUͰߦͳΘͣ"DUJWJUZʹ·ͱΊ͍ͨ
Activity Fragment 1 Fragment 4 Fragment 3 Fragment 2 SharedViewModel
ղܾࡦ
4IBSFE7JFX.PEFMͷྫ class SharedViewModel : ViewModel(){ val fragmentType = MutableLiveData<FragmentType>().apply {
this.value = FragmentType.FIRST } } enum class FragmentType(val type: String) { FIRST("first"), SECOND("second"), THIRD("third"), FOURTH("fourth") }
"DUJWJUZͰ0CTFSWF val sharedViewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java) sharedViewModel.fragmentType.observe(this, Observer { value ->
value?.let { Snackbar.make( findViewById(android.R.id.content), it.type, Snackbar.LENGTH_SHORT ).show() } }) 4IBSFE7JFX.PEFMͷGSBHNFOU5ZQF͕มߋ͞Εͨ࣌ʹ 4OBDLCBS͕දࣔ͞ΕΔ
'SBHNFOUͰQPTU7BMVF sharedViewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java) snackbarButton.clicks().subscribe { sharedViewModel.fragmentType.postValue(FragmentType.FIRST) } "DUJWJUZͰ0CTFSWF͍ͯ͠ΔͷͰɺ 'SBHNFOUͷ4IBSFE7JFX.PEFMͷΠϯελϯεΛ
7JFX.PEFM1SPWJEFSTPG ͷҾʹSFRVJSF"DUJWJUZ Λ ࢦఆ͠ͷ"DUJWJUZʹͻ͚ͮͯ࡞
݁Ռ 4IBSFE7JFX.PEFMΛ࡞Δ͜ͱͰɺ ϗετ͍ͯ͠Δ"DUJWJUZͱ ֤'SBHNFOUͰΛڞ༗ "DUJWJUZʹॲཧΛू
ը໘ભҠޙͱ ը໘ભҠ࣌ͷذॲཧ
՝ "DUJWJUZ͕࣋ͭ5PPMCBS'MPBUJOH"DUJPO#VUUPOͳͲͷ 7JFXͷڍಈΛ'SBHNFOUʹґଘͯ͠มߋͨ͠Γɺ 'SBHNFOUؒͷը໘ભҠ࣌ʹม͍͑ͨ ͔͠͠ɺ'SBHNFOUʹ"DUJWJUZ͕࣋ͭ7JFXͷڍಈͷॲཧΛ ॻ͖ͨ͘ͳ͍ why? val toolbar =
view.findViewById<Toolbar>(R.id.toolbar) requireActivity().setSupportActionBar(toolbar)
'SBHNFOUʹґଘͨ͠ڍಈͷมߋ ͳͷͰ'SBHNFOUͰॻ͖ͨ͘ͳΔ ͔͠͠5PPMCBS"DUJWJUZ͕อ༗ͯ͠ ͍ΔͷͰɺอकੑͷ؍͔Β "DUJWJUZͰॻ͖͘ )PNF'SBHNFOUͷ߹ͷΈ 5PPMCBSͷΔϘλϯԡԼ࣌ ͷڍಈΛมߋ͍ͨ͠
ղܾࡦ /BW$POUSPMMFSͷ DVSSFOU%FTUJOBUJPO BEE0O%FTUJOBUJPO$IBOHFE-JTUFOFS Λར༻
binding.toolbar .navigationClicks() .filter { navController.currentDestination != null } .subscribe {
when (navController.currentDestination!!.id) { R.id.homeFragment -> finish() R.id.secondFragment -> navController.navigate( SecondFragmentDirections.homeToFirst() ) } } navController = findNavController(R.id.container_fragment) 'SBHNFOU͝ͱʹ 5PPMCBSͷॲཧΛذ
navController.addOnDestinationChangedListener { _, destination, _ -> when (destination.id) { R.id.firstFragment
-> binding.toolbarButton.visibility = View.GONE R.id.secondFragment -> binding.toolbarButton.visibility = View.VISIBLE } } 'SBHNFOUؒͷը໘ભҠ࣌ ը໘ભҠ࣌ʹ5PPMCBSʹ͋Δ#VUUPOͷWJTJWJMJUZΛมߋ
'SBHNFOUʹґଘ͘͠'SBHNFOU ؒͷը໘ભҠʹΑΔ"DUJWJUZ͕࣋ͭ 7JFX ࠓճͷྫͩͱ5PPMCBS ͷ ڍಈมߋΛ'SBHNFOUʹॻ͔ͣʹ "DUJWJUZʹهࡌͰ͖ͨ ݁Ռ
#PUUPN/BWJHBUJPO7JFXͱͷ ซ༻࣌ͷঢ়ଶอ࣋
՝ #PUUPN/BWJHBUJPO7JFXͱ/BWJHBUJPO$PNQPOFOUΛซ༻ ͢Δ߹ɺσϑΥϧτͰλϒΓସ͑࣌ʹ'SBHNFOU͕࠶ ੜ͞Ε·͢ɻ Αͬͯɺ࠶ੜ͞Εͳ͍Α͏ʹ͢ΔͨΊʹ 'SBHNFOUͷঢ়ଶอ࣋Λ࣮͢Δඞཁ͕ ͋Γ·͢ɻ #PUUPN/BWJHBUJPO7JFX
ղܾࡦ 'SBHNFOU/BWJHBUPSΫϥεΛܧঝͨ͠ $VTUPN/BWJHBUPSΫϥεΛ࡞͠ɺ ঢ়ଶอ࣋͞ΕΔΑ͏ʹOBWJHBUF Λ্ॻ͖
@Navigator.Name("custom_fragment") class CustomNavigator( private val context: Context, private val manager:
FragmentManager, private val containerId: Int ) : FragmentNavigator(context, manager, containerId) { @Suppress("DEPRECATION") override fun navigate( destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras? ): NavDestination? { // ... } }
if (manager.isStateSaved) { return null } var className = destination.className
if (className[0] == '.') { className = context.packageName + className } val tag = destination.id.toString() val transaction = manager.beginTransaction() val currentFragment = manager.primaryNavigationFragment if (currentFragment != null) { transaction.hide(currentFragment) } var fragment = manager.findFragmentByTag(tag) if (fragment == null) { fragment = instantiateFragment(context, manager, className, args) transaction.add(containerId, fragment, tag) } fragment.arguments = args transaction.show(fragment) transaction.setPrimaryNavigationFragment(fragment) transaction.commit() return destination Ξχϝʔγϣϯͱ #BDL4UBDLपΓͷ ॲཧল͍͍ͯ·͢
class CustomNavHostFragment : NavHostFragment() { override fun createFragmentNavigator( ): Navigator<out
FragmentNavigator.Destination> { return CustomNavigator(requireContext(), childFragmentManager, id) } } /BW)PTU'SBHNFOUͷDSFBUF'SBHNFOU/BWJHBUPS ͷ Γ͕'SBHNFOU/BWJHBUPSͳͷͰܧঝͨ͠ $VTUPN/BW)PTU'SBHNFOUΛ࡞্͠ॻ͖
<navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/bottom_navigation" app:startDestination="@+id/tab1"> <custom_fragment android:id="@+id/tab1" android:name="hoge.HogeFragment" android:label="tab1" android:tag="tab1"
/> OBWJHBUJPOϨΠΞτ $VTUPN/BWJHBUPSΫϥεʹ͚ͭͨ !/BWJHBUPS/BNFΞϊςʔγϣϯͷҾʹࢦఆ͍ͯ͠Δ DVTUPN@GSBHNFOUΛར༻
<fragment android:id="@+id/bottom_navigation_view_fragment" android:name="hoge.navigation.CustomNavHostFragment" android:layout_width="match_parent" android:layout_height="wrap_content" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@id/bottom_navigation_view" app:layout_constraintTop_toBottomOf="@+id/toolbar" app:navGraph="@navigation/navigation" />
<com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/windowBackground" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/bottom_navigation_menu" /> BOESPJEYOBWJHBUJPOGSBHNFOU/BW)PTU'SBHNFOU Ͱͳࣗ͘࡞ͨ͠$VTUPN/BW)PTU'SBHNFOUΛࢦఆ
݁Ռ 'SBHNFOUͷঢ়ଶอ࣋ʹޭ
࠷ޙʹ /BWJHBUJPO$PNQPOFOUΛ͏ͱ "DUJWJUZ'SBHNFOUؒͷը໘ભҠΛ γϯϓϧʹ࣮Ͱ͖Δ ࣮ફͰಋೖ͢ΔͱͳΔͱ ӡ༻อकੑΛߟྀ͢ΔΑ͏ʹͳΓ ҙ͕ඞཁ