Skip to content

Commit 2cdd791

Browse files
committed
add detail page and navigation
1 parent e21e4fd commit 2cdd791

16 files changed

+274
-38
lines changed

10-mars-photos/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_5_31.xml

-13
This file was deleted.

10-mars-photos/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_5_31.xml

-13
This file was deleted.

10-mars-photos/.idea/navEditor.xml

+52
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

10-mars-photos/app/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
plugins {
1818
id 'com.android.application'
1919
id 'kotlin-android'
20+
id 'kotlin-parcelize'
2021
id 'kotlin-kapt'
22+
id 'androidx.navigation.safeargs'
2123
}
2224

2325
android {
@@ -75,4 +77,5 @@ dependencies {
7577

7678
implementation 'com.google.android.material:material:1.4.0'
7779
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
80+
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
7881
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.example.android.marsphotos.detail
2+
3+
import android.os.Bundle
4+
import androidx.fragment.app.Fragment
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import androidx.lifecycle.ViewModelProvider
9+
import com.example.android.marsphoto.detail.DetailViewModelFactory
10+
import com.example.android.marsphotos.R
11+
import com.example.android.marsphotos.databinding.FragmentDetailBinding
12+
13+
/**
14+
* A simple [Fragment] subclass.
15+
* Use the [DetailFragment.newInstance] factory method to
16+
* create an instance of this fragment.
17+
*/
18+
class DetailFragment : Fragment() {
19+
20+
override fun onCreateView(
21+
inflater: LayoutInflater, container: ViewGroup?,
22+
savedInstanceState: Bundle?
23+
): View? {
24+
val application = requireNotNull(activity).application
25+
val binding = FragmentDetailBinding.inflate(inflater)
26+
binding.lifecycleOwner = this
27+
28+
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
29+
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
30+
binding.viewModel = ViewModelProvider(
31+
this, viewModelFactory).get(DetailViewModel::class.java)
32+
33+
return binding.root
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.example.android.marsphotos.detail
2+
3+
import android.app.Application
4+
import androidx.lifecycle.AndroidViewModel
5+
import androidx.lifecycle.LiveData
6+
import androidx.lifecycle.MutableLiveData
7+
import com.example.android.marsphotos.network.MarsPhoto
8+
9+
class DetailViewModel(marsProperty: MarsPhoto, app: Application) : AndroidViewModel(app) {
10+
private val _selectedProperty = MutableLiveData<MarsPhoto>()
11+
val selectedProperty: LiveData<MarsPhoto>
12+
get() = _selectedProperty
13+
14+
init {
15+
_selectedProperty.value = marsProperty
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.example.android.marsphoto.detail
2+
3+
import android.app.Application
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.ViewModelProvider
6+
import com.example.android.marsphotos.detail.DetailViewModel
7+
import com.example.android.marsphotos.network.MarsPhoto
8+
9+
/**
10+
* Simple ViewModel factory that provides the MarsProperty and context to the ViewModel.
11+
*/
12+
class DetailViewModelFactory(
13+
private val marsProperty: MarsPhoto,
14+
private val application: Application) : ViewModelProvider.Factory {
15+
@Suppress("unchecked_cast")
16+
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
17+
if (modelClass.isAssignableFrom(DetailViewModel::class.java)) {
18+
return DetailViewModel(marsProperty, application) as T
19+
}
20+
throw IllegalArgumentException("Unknown ViewModel class")
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package com.example.android.marsphotos.network
22

3+
import android.os.Parcelable
34
import com.squareup.moshi.Json
5+
import kotlinx.android.parcel.Parcelize
46

7+
@Parcelize
58
data class MarsPhoto(
69
val id: String,
710
@Json(name = "img_src") val imgSrcUrl: String,
811
val type: String,
912
val price: Double
10-
) {
13+
) : Parcelable {
1114
val isRental
1215
get() = type == "rent"
1316
}

10-mars-photos/app/src/main/java/com/example/android/marsphotos/overview/OverviewFragment.kt

+15-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package com.example.android.marsphotos.overview
1919
import android.os.Bundle
2020
import android.view.*
2121
import androidx.fragment.app.Fragment
22+
import androidx.lifecycle.Observer
2223
import androidx.lifecycle.ViewModelProvider
24+
import androidx.navigation.fragment.findNavController
2325
import com.example.android.marsphotos.R
2426
import com.example.android.marsphotos.databinding.FragmentOverviewBinding
2527
import com.example.android.marsphotos.network.MarsApiFilter
@@ -49,7 +51,19 @@ class OverviewFragment : Fragment() {
4951
binding.viewModel = viewModel
5052

5153
// Sets the adapter of the photosGrid RecyclerView
52-
binding.photosGrid.adapter = PhotoGridAdapter()
54+
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
55+
viewModel.displayPropertyDetails(it)
56+
})
57+
58+
// Connects the fragments
59+
viewModel.navigateToSelectedProperty.observe(this, Observer {
60+
if (null != it) {
61+
this.findNavController().navigate(
62+
OverviewFragmentDirections.actionShowDetail(it)
63+
)
64+
viewModel.displayPropertyDetailsComplete()
65+
}
66+
})
5367

5468
setHasOptionsMenu(true)
5569
return binding.root

10-mars-photos/app/src/main/java/com/example/android/marsphotos/overview/OverviewViewModel.kt

+15
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class OverviewViewModel : ViewModel() {
4949
val properties: LiveData<List<MarsPhoto>>
5050
get() = _properties
5151

52+
// LiveData for detail page
53+
private val _navigateToSelectedProperty = MutableLiveData<MarsPhoto>()
54+
val navigateToSelectedProperty: LiveData<MarsPhoto>
55+
get() = _navigateToSelectedProperty
56+
5257
/**
5358
* Call getMarsPhotos() on init so we can display status immediately.
5459
*/
@@ -82,4 +87,14 @@ class OverviewViewModel : ViewModel() {
8287
fun updateFilter(filter: MarsApiFilter) {
8388
getMarsPhotos(filter)
8489
}
90+
91+
// display details
92+
fun displayPropertyDetails(marsProperty : MarsPhoto) {
93+
_navigateToSelectedProperty.value = marsProperty
94+
}
95+
96+
// reset navigation
97+
fun displayPropertyDetailsComplete() {
98+
_navigateToSelectedProperty.value = null
99+
}
85100
}

10-mars-photos/app/src/main/java/com/example/android/marsphotos/overview/PhotoGridAdapter.kt

+9-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import androidx.recyclerview.widget.RecyclerView
88
import com.example.android.marsphotos.databinding.GridViewItemBinding
99
import com.example.android.marsphotos.network.MarsPhoto
1010

11-
class PhotoGridAdapter : ListAdapter<MarsPhoto,
11+
class PhotoGridAdapter(private val onClickListener: OnClickListener) : ListAdapter<MarsPhoto,
1212
PhotoGridAdapter.MarsPhotoViewHolder>(DiffCallback) {
13+
1314
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPhotoViewHolder {
1415
return MarsPhotoViewHolder(GridViewItemBinding.inflate(
1516
LayoutInflater.from(parent.context)
@@ -18,6 +19,9 @@ class PhotoGridAdapter : ListAdapter<MarsPhoto,
1819

1920
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPhotoViewHolder, position: Int) {
2021
val marsPhoto = getItem(position)
22+
holder.itemView.setOnClickListener {
23+
onClickListener.onClick(marsPhoto)
24+
}
2125
holder.bind(marsPhoto)
2226
}
2327

@@ -37,4 +41,8 @@ class PhotoGridAdapter : ListAdapter<MarsPhoto,
3741
binding.executePendingBindings()
3842
}
3943
}
44+
45+
class OnClickListener(val clickListener: (marsProperty: MarsPhoto) -> Unit) {
46+
fun onClick(marsProperty: MarsPhoto) = clickListener(marsProperty)
47+
}
4048
}

10-mars-photos/app/src/main/res/layout/activity_main.xml

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
~ limitations under the License.
1717
-->
1818

19-
<androidx.fragment.app.FragmentContainerView
20-
xmlns:android="http://schemas.android.com/apk/res/android"
21-
xmlns:tools="http://schemas.android.com/tools"
22-
android:id="@+id/overviewFragment"
23-
android:name="com.example.android.marsphotos.overview.OverviewFragment"
19+
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
20+
xmlns:app="http://schemas.android.com/apk/res-auto"
21+
android:id="@+id/nav_host_fragment"
22+
android:name="androidx.navigation.fragment.NavHostFragment"
2423
android:layout_width="match_parent"
2524
android:layout_height="match_parent"
26-
tools:layout="@layout/fragment_overview" />
25+
app:defaultNavHost="true"
26+
app:navGraph="@navigation/nav_graph" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<layout xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
xmlns:tools="http://schemas.android.com/tools">
6+
7+
<data>
8+
<variable
9+
name="viewModel"
10+
type="com.example.android.marsphotos.detail.DetailViewModel" />
11+
</data>
12+
13+
<ScrollView
14+
android:layout_width="match_parent"
15+
android:layout_height="match_parent"
16+
tools:context=".DetailFragment">
17+
18+
<androidx.constraintlayout.widget.ConstraintLayout
19+
android:layout_width="match_parent"
20+
android:layout_height="wrap_content"
21+
android:padding="16dp">
22+
23+
<ImageView
24+
android:id="@+id/main_photo_image"
25+
android:layout_width="0dp"
26+
android:layout_height="266dp"
27+
android:scaleType="centerCrop"
28+
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"
29+
app:layout_constraintEnd_toEndOf="parent"
30+
app:layout_constraintStart_toStartOf="parent"
31+
app:layout_constraintTop_toTopOf="parent"
32+
tools:src="@tools:sample/backgrounds/scenic" />
33+
34+
<TextView
35+
android:id="@+id/property_type_text"
36+
android:layout_width="wrap_content"
37+
android:layout_height="wrap_content"
38+
android:layout_marginTop="16dp"
39+
android:textColor="#de000000"
40+
android:textSize="39sp"
41+
android:text="nothing"
42+
app:layout_constraintStart_toStartOf="parent"
43+
app:layout_constraintTop_toBottomOf="@+id/main_photo_image"
44+
tools:text="To Rent" />
45+
46+
<TextView
47+
android:id="@+id/price_value_text"
48+
android:layout_width="wrap_content"
49+
android:layout_height="wrap_content"
50+
android:layout_marginTop="8dp"
51+
android:textColor="#de000000"
52+
android:textSize="20sp"
53+
android:text="nothing"
54+
app:layout_constraintStart_toStartOf="parent"
55+
app:layout_constraintTop_toBottomOf="@+id/property_type_text"
56+
tools:text="$100,000" />
57+
58+
</androidx.constraintlayout.widget.ConstraintLayout>
59+
</ScrollView>
60+
</layout>

0 commit comments

Comments
 (0)