Skip to content

Commit

Permalink
Apply the new design for List-detail sample (#15)
Browse files Browse the repository at this point in the history
* Start the new design for list detail

* Update the detail page

* Update UI

* Switch to the new control to support full screen portrait mode

* Add back button

* Update
  • Loading branch information
joyl1216 authored Dec 2, 2020
1 parent 0540e47 commit eb1c348
Show file tree
Hide file tree
Showing 33 changed files with 338 additions and 240 deletions.
5 changes: 4 additions & 1 deletion ListDetail/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ dependencies {
implementation androidxDependencies.constraintLayout
implementation androidxDependencies.ktxCore
implementation androidxDependencies.ktxFragment
implementation androidxDependencies.recyclerView

implementation microsoftDependencies.dualScreenLayout
// TODO: temporary change, will be updated in the next PR
// implementation microsoftDependencies.dualScreenLayout
implementation "com.microsoft.device.dualscreen:layouts:1.0.0-alpha02"

testImplementation testDependencies.junit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,16 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RatingBar
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.microsoft.device.display.samples.listdetail.model.MovieMock
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.microsoft.device.display.samples.listdetail.model.SelectionViewModel
import com.microsoft.device.dualscreen.core.ScreenHelper

class ItemDetailFragment : Fragment() {
private lateinit var movieMock: MovieMock

companion object {
internal fun newInstance(movieMock: MovieMock) = ItemDetailFragment().apply {
arguments = Bundle().apply {
this.putSerializable(MovieMock.KEY, movieMock)
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
movieMock = it.getSerializable(MovieMock.KEY) as MovieMock
}
}
private lateinit var imageView: ImageView

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -44,16 +32,36 @@ class ItemDetailFragment : Fragment() {
container,
false
)
val tvTitle = view.findViewById<TextView>(R.id.tvTitle)
val tvBody = view.findViewById<TextView>(R.id.tvBody)
val ratingBar = view.findViewById<RatingBar>(R.id.rating)
val image = view.findViewById<ImageView>(R.id.image)
imageView = view.findViewById(R.id.imageView)

tvTitle.text = movieMock.title
tvBody.text = movieMock.body
ratingBar.rating = 2f
image.setImageResource(R.drawable.ic_movie_black_24dp)
if (!ScreenHelper.isDualMode(requireActivity())) {
val toolbar = view.findViewById<Toolbar>(R.id.detail_toolbar)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
toolbar.setNavigationOnClickListener { closeFragment() }
}

return view
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)

val viewModel = ViewModelProvider(requireActivity()).get(SelectionViewModel::class.java)
viewModel.getSelectedItemLiveData().observe(
viewLifecycleOwner,
Observer {
it?.let { imageView.setImageResource(it) }
}
)
}

private fun closeFragment() {
parentFragmentManager.beginTransaction()
.replace(
R.id.first_container_id,
ItemsListFragment(),
null
).addToBackStack(null)
.commit()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.microsoft.device.display.samples.listdetail.model.DataProvider
import com.microsoft.device.display.samples.listdetail.model.MovieMock
import com.microsoft.device.dualscreen.layout.ScreenHelper
import com.microsoft.device.display.samples.listdetail.model.ImageAdapter
import com.microsoft.device.display.samples.listdetail.model.SelectionViewModel
import com.microsoft.device.dualscreen.core.ScreenHelper

class ItemsListFragment : Fragment(), AdapterView.OnItemClickListener {
private var arrayAdapter: ArrayAdapter<MovieMock>? = null
private var listView: ListView? = null
private lateinit var movieMocks: ArrayList<MovieMock>
class ItemsListFragment : Fragment() {
private var imageAdapter: ImageAdapter? = null
private lateinit var images: ArrayList<Int>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
movieMocks = DataProvider.movieMocks
images = DataProvider.imageIDs
activity?.let {
arrayAdapter = ArrayAdapter(
imageAdapter = ImageAdapter(
it,
android.R.layout.simple_list_item_activated_1,
movieMocks
images,
::onItemClick
)
}
}
Expand All @@ -41,43 +41,35 @@ class ItemsListFragment : Fragment(), AdapterView.OnItemClickListener {
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_items_list, container, false)
listView = view.findViewById(R.id.list_view)
listView?.let {
it.adapter = arrayAdapter
it.choiceMode = ListView.CHOICE_MODE_SINGLE
it.onItemClickListener = this
}
val recyclerView = view.findViewById<RecyclerView>(R.id.image_list)

val layoutManager = StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL)
layoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE
recyclerView.layoutManager = layoutManager
recyclerView.adapter = imageAdapter

return view
}

private fun setSelectedItem(position: Int) {
listView?.setItemChecked(position, true)
}
private fun onItemClick(item: Int) {
val viewModel = ViewModelProvider(requireActivity()).get(SelectionViewModel::class.java)
viewModel.setSelectedItemLiveData(item)

override fun onItemClick(adapterView: AdapterView<*>, item: View, position: Int, rowId: Long) {
val movieMock = arrayAdapter?.getItem(position)
setSelectedItem(position)
movieMock?.let { movie ->
activity?.let { activity ->
if (ScreenHelper.isDualMode(activity)) {
parentFragmentManager.beginTransaction()
.replace(
R.id.dual_screen_end_container_id,
ItemDetailFragment.newInstance(movie), null
).commit()
} else {
startDetailsFragment(movie)
}
}
if (ScreenHelper.isDualMode(requireActivity()) && !MainActivity.isPortrait(requireActivity())) {
parentFragmentManager.beginTransaction()
.replace(
R.id.second_container_id,
ItemDetailFragment(),
null
).commit()
} else {
parentFragmentManager.beginTransaction()
.replace(
R.id.first_container_id,
ItemDetailFragment(),
null
).addToBackStack(null)
.commit()
}
}

private fun startDetailsFragment(movieMock: MovieMock) {
parentFragmentManager.beginTransaction()
.replace(
R.id.single_screen_container_id,
ItemDetailFragment.newInstance(movieMock), null
).addToBackStack(null)
.commit()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,55 @@

package com.microsoft.device.display.samples.listdetail

import android.content.Context
import android.os.Bundle
import android.view.Surface
import androidx.appcompat.app.AppCompatActivity
import com.microsoft.device.display.samples.listdetail.model.DataProvider
import com.microsoft.device.dualscreen.layout.ScreenHelper
import androidx.lifecycle.ViewModelProvider
import com.microsoft.device.display.samples.listdetail.model.SelectionViewModel
import com.microsoft.device.dualscreen.core.ScreenHelper

class MainActivity : AppCompatActivity() {
companion object {
fun isPortrait(context: Context): Boolean {
return ScreenHelper.getCurrentRotation(context) == Surface.ROTATION_90 ||
ScreenHelper.getCurrentRotation(context) == Surface.ROTATION_270
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

if (!ScreenHelper.isDualMode(this)) {
if (ScreenHelper.isDualMode(this) && !isPortrait(applicationContext)) {
supportFragmentManager
.beginTransaction()
.replace(
R.id.single_screen_container_id,
R.id.first_container_id,
ItemsListFragment(),
null
)
.commit()
} else {
).commit()
supportFragmentManager
.beginTransaction()
.replace(
R.id.dual_screen_start_container_id,
ItemsListFragment(),
null
R.id.second_container_id,
ItemDetailFragment()
).commit()
} else {
supportFragmentManager
.beginTransaction()
.replace(
R.id.dual_screen_end_container_id,
ItemDetailFragment.newInstance(DataProvider.movieMocks[0]),
R.id.first_container_id,
ItemsListFragment(),
null
).commit()
)
.commit()
}

// set the first image as the default selection if none is selected
val viewModel = ViewModelProvider(this).get(SelectionViewModel::class.java)
if (viewModel.getSelectedItemLiveData().value == 0) {
viewModel.setSelectedItemLiveData(R.drawable.image_1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@

package com.microsoft.device.display.samples.listdetail.model

import com.microsoft.device.display.samples.listdetail.R
import java.util.ArrayList

object DataProvider {
val movieMocks: ArrayList<MovieMock>
val imageIDs: ArrayList<Int>
get() {
val items = ArrayList<MovieMock>()
items.add(MovieMock("Item 1", "This is the first item"))
items.add(MovieMock("Item 2", "This is the second item"))
items.add(MovieMock("Item 3", "This is the third item"))
val items = ArrayList<Int>()
items.add(R.drawable.image_1)
items.add(R.drawable.image_2)
items.add(R.drawable.image_3)
items.add(R.drawable.image_4)
items.add(R.drawable.image_5)
items.add(R.drawable.image_6)
items.add(R.drawable.image_7)
items.add(R.drawable.image_8)
items.add(R.drawable.image_9)
items.add(R.drawable.image_10)
items.add(R.drawable.image_11)
items.add(R.drawable.image_12)
return items
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.microsoft.device.display.samples.listdetail.model

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.microsoft.device.display.samples.listdetail.R

class ImageAdapter(
context: Context,
private val images: List<Int>,
private val onClick: (image: Int) -> Unit
) : RecyclerView.Adapter<ImageAdapter.ImageViewHolder>() {

private val layoutInflater = LayoutInflater.from(context)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ImageViewHolder(
layoutInflater.inflate(R.layout.layout_image_item, parent, false)
)

override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
holder.bind(images[position], onClick)
}

override fun getItemCount() = images.size

class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val imageView = itemView.findViewById(R.id.image_item) as ImageView

fun bind(image: Int, onClick: (image: Int) -> Unit) {
imageView.setImageResource(image)
itemView.setOnClickListener { onClick(image) }
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.microsoft.device.display.samples.listdetail.model

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class SelectionViewModel : ViewModel() {
private val selectedItemLiveData: MutableLiveData<Int?> = MutableLiveData(0)

fun getSelectedItemLiveData(): LiveData<Int?> {
return this.selectedItemLiveData
}

fun setSelectedItemLiveData(selectedItem: Int?) {
selectedItemLiveData.value = selectedItem
}
}
Binary file added ListDetail/src/main/res/drawable/camera_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions ListDetail/src/main/res/drawable/ic_arrow_back.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"
android:fillColor="@color/white"/>
</vector>
Loading

0 comments on commit eb1c348

Please sign in to comment.