Created
September 26, 2019 16:06
-
-
Save saeed-younus/91f681cd3bec83158695ac3196fb2cc6 to your computer and use it in GitHub Desktop.
Selection Recyclerview List using live data, viewmodel and list adapter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<com.google.android.material.appbar.AppBarLayout | |
android:id="@+id/app_bar_layout" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:background="#FFFFFF" | |
app:liftOnScroll="true"> | |
<androidx.constraintlayout.widget.ConstraintLayout | |
android:id="@+id/cl_toolbar" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:background="@color/cardForeground" | |
android:gravity="center_vertical" | |
android:orientation="horizontal"> | |
<ImageView | |
android:id="@+id/image_back" | |
style="@style/ToolbarIcon" | |
android:layout_marginTop="8dp" | |
android:layout_marginStart="8dp" | |
android:src="@drawable/ic_left_arrow" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toTopOf="parent" /> | |
<TextView | |
style="@style/ToolbarTitle" | |
android:layout_width="0dp" | |
android:layout_height="0dp" | |
android:layout_marginStart="8dp" | |
android:layout_marginEnd="8dp" | |
android:gravity="center_vertical" | |
android:text="Mileage" | |
app:layout_constraintBottom_toBottomOf="@id/image_back" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toEndOf="@id/image_back" | |
app:layout_constraintTop_toTopOf="@id/image_back" /> | |
<ImageView | |
android:id="@+id/image_delete" | |
style="@style/ToolbarIcon" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:layout_marginTop="8dp" | |
android:layout_marginBottom="8dp" | |
android:src="@drawable/ic_delete" | |
android:layout_marginEnd="8dp" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toEndOf="parent" /> | |
<com.google.android.material.button.MaterialButton | |
android:id="@+id/text_select" | |
style="@style/FlatButton" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="8dp" | |
android:layout_marginBottom="8dp" | |
android:layout_marginTop="8dp" | |
android:text="Select" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toBottomOf="@+id/image_back" /> | |
<com.google.android.material.button.MaterialButton | |
android:id="@+id/text_select_all" | |
style="@style/FlatButton" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="8dp" | |
android:layout_marginBottom="8dp" | |
android:layout_marginTop="8dp" | |
android:text="Select All" | |
android:visibility="invisible" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toBottomOf="@+id/image_back" /> | |
<View | |
android:id="@+id/divider" | |
android:layout_width="1dp" | |
android:layout_height="0dp" | |
android:layout_marginTop="4dp" | |
android:layout_marginBottom="4dp" | |
android:layout_marginStart="8dp" | |
android:background="@color/colorPrimary" | |
app:layout_constraintBottom_toBottomOf="@id/text_create_list" | |
app:layout_constraintStart_toEndOf="@id/text_select_all" | |
app:layout_constraintTop_toTopOf="@id/text_create_list" /> | |
<com.google.android.material.button.MaterialButton | |
android:id="@+id/text_create_list" | |
style="@style/FlatButton.Icon" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="8dp" | |
android:layout_marginBottom="8dp" | |
android:layout_marginTop="8dp" | |
android:text="Create New List" | |
app:icon="@drawable/ic_plus_circle" | |
android:background="?attr/selectableItemBackground" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintStart_toEndOf="@id/divider" /> | |
</androidx.constraintlayout.widget.ConstraintLayout> | |
</com.google.android.material.appbar.AppBarLayout> | |
<androidx.core.widget.NestedScrollView | |
android:id="@+id/nested_scrollview" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" > | |
<androidx.recyclerview.widget.RecyclerView | |
android:id="@+id/rv_list" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" /> | |
</androidx.core.widget.NestedScrollView> | |
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"> | |
<View | |
android:layout_width="0dp" | |
android:layout_height="0dp" | |
android:elevation="10dp" | |
android:background="?attr/selectableItemBackground" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toTopOf="parent" /> | |
<ImageView | |
android:id="@+id/image_selector" | |
android:layout_width="24dp" | |
android:layout_height="24dp" | |
android:layout_marginStart="8dp" | |
android:src="@drawable/ic_circle_empty" | |
android:visibility="gone" | |
app:layout_constraintBottom_toBottomOf="@id/card_item" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toTopOf="@id/card_item" /> | |
<com.google.android.material.card.MaterialCardView | |
android:id="@+id/card_item" | |
style="@style/itemCard" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="8dp" | |
android:layout_marginTop="16dp" | |
android:layout_marginEnd="8dp" | |
android:layout_marginBottom="16dp" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toEndOf="@id/image_selector" | |
app:layout_constraintTop_toTopOf="parent"> | |
<androidx.constraintlayout.widget.ConstraintLayout | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"> | |
<TextView | |
android:id="@+id/text_date" | |
style="@style/accentText" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:layout_marginStart="16dp" | |
android:padding="0dp" | |
android:text="02-01-2019" | |
android:singleLine="true" | |
android:ellipsize="end" | |
android:textStyle="bold" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toStartOf="@+id/text_mile_number" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toTopOf="parent" /> | |
<TextView | |
android:id="@+id/text_mile_number" | |
style="@style/primaryText.Medium" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:padding="0dp" | |
android:text="45.2" | |
android:textAlignment="center" | |
android:textStyle="bold" | |
android:layout_marginTop="16dp" | |
app:layout_constraintTop_toTopOf="parent" | |
app:layout_constraintBottom_toTopOf="@+id/text_mile" | |
app:layout_constraintEnd_toEndOf="@+id/text_mile" | |
app:layout_constraintStart_toStartOf="@+id/text_mile" /> | |
<TextView | |
android:id="@+id/text_mile" | |
style="@style/primaryText" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:text="MILES" | |
android:singleLine="true" | |
android:ellipsize="end" | |
android:textAllCaps="true" | |
android:layout_marginBottom="16dp" | |
android:layout_marginEnd="16dp" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintTop_toBottomOf="@id/text_mile_number" | |
app:layout_constraintEnd_toStartOf="@id/divider" /> | |
<ImageView | |
android:id="@+id/image_arrow" | |
style="@style/imageIcon" | |
android:layout_width="40dp" | |
android:layout_height="40dp" | |
android:background="#fff" | |
android:padding="8dp" | |
android:src="@drawable/ic_right_arrow" | |
android:tint="@color/iconSecondary" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintTop_toTopOf="parent" /> | |
<TextView | |
android:id="@+id/text_property_number" | |
style="@style/primaryText.Medium" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:padding="0dp" | |
android:text="56" | |
android:textAlignment="center" | |
android:textStyle="bold" | |
android:singleLine="true" | |
android:ellipsize="end" | |
android:layout_marginTop="16dp" | |
app:layout_constraintBottom_toTopOf="@+id/text_property" | |
app:layout_constraintEnd_toEndOf="@+id/text_property" | |
app:layout_constraintStart_toStartOf="@+id/text_property" | |
app:layout_constraintTop_toTopOf="parent" /> | |
<TextView | |
android:id="@+id/text_property" | |
style="@style/primaryText" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:text="Properties" | |
android:textAllCaps="true" | |
android:singleLine="true" | |
android:ellipsize="end" | |
android:layout_marginBottom="16dp" | |
app:layout_constraintTop_toBottomOf="@id/text_property_number" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toStartOf="@id/image_arrow" /> | |
<View | |
android:id="@+id/divider" | |
android:layout_width="1dp" | |
android:layout_height="0dp" | |
android:layout_marginTop="16dp" | |
android:layout_marginEnd="16dp" | |
android:layout_marginBottom="16dp" | |
android:background="@color/iconSecondary" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toStartOf="@id/text_property" | |
app:layout_constraintTop_toTopOf="parent" /> | |
</androidx.constraintlayout.widget.ConstraintLayout> | |
</com.google.android.material.card.MaterialCardView> | |
</androidx.constraintlayout.widget.ConstraintLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum class ListState(val state: Int) { | |
OPEN_SELECTION(0), | |
CLOSED_SELECTION(0), | |
SELECT_ALL(0) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.ImageView | |
import androidx.recyclerview.widget.DiffUtil | |
import androidx.recyclerview.widget.ListAdapter | |
import androidx.recyclerview.widget.RecyclerView | |
class SelectionListAdapter(private val onItemClickListener: () -> Unit) : | |
ListAdapter<String, SelectionListAdapter.SelectionViewHolder>( | |
SelectionDiffUtil() | |
) { | |
private var listState: ListState = ListState.CLOSED_SELECTION | |
override fun onCreateViewHolder( | |
parent: ViewGroup, | |
viewType: Int | |
): SelectionViewHolder { | |
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) | |
return SelectionViewHolder(view, onItemClickListener) | |
} | |
override fun getItemViewType(position: Int): Int { | |
return R.layout.item_selection | |
} | |
fun setListState(state: ListState) { | |
listState = state | |
this.notifyDataSetChanged() | |
} | |
override fun onBindViewHolder( | |
holder: SelectionViewHolder, | |
position: Int | |
) { | |
holder.bind(getItem(position), listState) | |
} | |
class SelectionViewHolder(val view: View, private val onItemClickListener: () -> Unit) : | |
RecyclerView.ViewHolder(view) { | |
// TODO: This is for test purpose Remove isSelected from here when we apply databinding | |
var isSelected = false | |
fun bind(item: String, listState: ListState) { | |
view.setOnClickListener { | |
if (listState == ListState.OPEN_SELECTION || listState == ListState.SELECT_ALL) { | |
isSelected = !isSelected | |
isSelectedItem(isSelected) | |
} else { | |
onItemClickListener.invoke() | |
} | |
} | |
when (listState) { | |
ListState.OPEN_SELECTION -> { | |
enableSelection(enable = true) | |
} | |
ListState.SELECT_ALL -> { | |
isSelected = true | |
isSelectedItem(isSelected) | |
enableSelection(enable = true) | |
} | |
ListState.CLOSED_SELECTION -> { | |
isSelected = false | |
isSelectedItem(isSelected) | |
enableSelection(enable = false) | |
} | |
} | |
} | |
private fun isSelectedItem(selected: Boolean) { | |
val imageViewItemSelector = view.findViewById<ImageView>(R.id.image_selector) | |
if (selected) { | |
imageViewItemSelector.setImageResource(R.drawable.ic_circle_tick) | |
} else { | |
imageViewItemSelector.setImageResource(R.drawable.ic_circle_empty) | |
} | |
} | |
private fun enableSelection(enable: Boolean) { | |
val imageViewItemSelector = view.findViewById<ImageView>(R.id.image_selector) | |
if (enable) { | |
imageViewItemSelector.visibility = View.VISIBLE | |
} else { | |
imageViewItemSelector.visibility = View.GONE | |
} | |
} | |
} | |
class SelectionDiffUtil : DiffUtil.ItemCallback<String>() { | |
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean { | |
return oldItem == newItem | |
} | |
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean { | |
return oldItem == newItem | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Bundle | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import androidx.core.widget.NestedScrollView | |
import androidx.fragment.app.Fragment | |
import androidx.lifecycle.Observer | |
import androidx.lifecycle.ViewModelProviders | |
import androidx.recyclerview.widget.LinearLayoutManager | |
class MileageFragment : Fragment() { | |
private lateinit var listAdapter: SelectionListAdapter | |
private lateinit var viewModel: SelectionListViewModel | |
override fun onCreateView( | |
inflater: LayoutInflater, | |
container: ViewGroup?, | |
savedInstanceState: Bundle? | |
): View? { | |
return inflater.inflate(R.layout.fragment_selection_list, container, false) | |
} | |
override fun onActivityCreated(savedInstanceState: Bundle?) { | |
super.onActivityCreated(savedInstanceState) | |
viewModel = ViewModelProviders.of(this).get(SelectionListViewModel::class.java) | |
setupAdapter() | |
viewModel.listState.observe(this, Observer { | |
listAdapter.setListState(it) | |
}) | |
nested_scrollview.setOnScrollChangeListener { v: NestedScrollView?, | |
scrollX: Int, | |
scrollY: Int, | |
oldScrollX: Int, | |
oldScrollY: Int -> | |
if (scrollY <= 0) { | |
app_bar_layout.setLifted(false) | |
} | |
} | |
image_back.setOnClickListener { | |
if (viewModel.listState.value == ListState.OPEN_SELECTION || | |
viewModel.listState.value == ListState.SELECT_ALL | |
) { | |
text_select.visibility = View.VISIBLE | |
text_select_all.visibility = View.INVISIBLE | |
viewModel.setListState(ListState.CLOSED_SELECTION) | |
} | |
} | |
text_select_all.setOnClickListener { | |
viewModel.setListState(ListState.SELECT_ALL) | |
} | |
text_select.setOnClickListener { | |
text_select.visibility = View.INVISIBLE | |
text_select_all.visibility = View.VISIBLE | |
viewModel.setListState(ListState.OPEN_SELECTION) | |
} | |
} | |
private fun setupAdapter() { | |
listAdapter = SelectionListAdapter { | |
//Navigation | |
} | |
rv_list.apply { | |
adapter = listAdapter | |
layoutManager = LinearLayoutManager(requireContext()) | |
} | |
listAdapter.submitList(listOf("", "", "", "", "", "", "", "")) | |
} | |
companion object { | |
fun newInstance() = MileageFragment() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.Application | |
import androidx.lifecycle.AndroidViewModel | |
import androidx.lifecycle.LiveData | |
import androidx.lifecycle.MutableLiveData | |
class SelectionListViewModel(app: Application) : AndroidViewModel(app) { | |
private val _listState: MutableLiveData<ListState> = MutableLiveData(ListState.CLOSED_SELECTION) | |
val listState: LiveData<ListState> = _listState | |
fun setListState(state: ListState) { | |
_listState.value = state | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment