How to use GridLayoutManager in RecyclerView on Android - android

In my application I have recyclerView and for this I should use GridLayoutManager and set 2 items per column.
I want set this 2 items fit of screen, each items sticky to screen. Such as below image:
I write below codes, but after run application show me such as this :
And I want set 120dp for layout_width and layout_height!
My XML code :
<?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:id="#+id/rowMasterQuestion_root"
android:layout_width="#dimen/_120mdp"
android:layout_height="#dimen/_120mdp"
android:background="#drawable/gray_border">
<TextView
android:id="#+id/rowMasterQuestion_title"
android:layout_width="#dimen/_120mdp"
android:layout_height="wrap_content"
android:layout_margin="#dimen/_15mdp"
android:gravity="center_horizontal"
android:textColor="#color/black"
android:textSize="#dimen/_9font_mdp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/rowMasterQuestion_answer"
android:layout_width="wrap_content"
android:layout_height="#dimen/_15mdp"
android:layout_marginLeft="#dimen/_10mdp"
android:layout_marginRight="#dimen/_10mdp"
android:layout_marginBottom="#dimen/_20mdp"
android:gravity="center"
android:paddingLeft="#dimen/_5mdp"
android:paddingRight="#dimen/_5mdp"
android:textColor="#color/white"
android:textSize="#dimen/_8font_mdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
My Activity codes :
requestMain_childList.apply {
layoutManager = GridLayoutManager(this#RequestMainPage, 2, GridLayoutManager.VERTICAL, false)
hasFixedSize()
adapter = masterChildAdapter
}
How can I fix this issue?

This is tricky because you are wanting a different layout gravity for each item, depending on the column. I think that will have to be set in the RecyclerView.Adapter.onBindViewHolder function.
First, wrap the ConstraintLayout of your item view in a FrameLayout. Make the width of the outer FrameLayout match_parent so it will fill its column in the RecyclerView. You can also give the FrameLayout the amount of start/end padding you want to hold the items away from the sides of the screen.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/wrappedView"
android:layout_width="#dimen/_120mdp"
android:layout_height="#dimen/_120mdp"
...>
...
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
Then in onBindViewHolder, you can set the gravity of the inner view to the left or right depending on which column it is in (even or odd data position).
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// ...
val innerView = holder.wrappedView // or however you have it stored in view holder
(innerView.layoutParams as FrameLayout.LayoutParams).gravity = Gravity.CENTER_VERTICAL or
(if (position % 2 == 0) Gravity.START else Gravity.END)
}
Note, if you want the items to be centered in their columns instead, this is much easier. You can just set the gravity of the inner view to center in the XML and you don't have to do anything in the Adapter.

Related

Identify first visible view inside viewgroup

I have several TextViews inside ConstraintLayout. The visibility of those TextViews are set at runtime based upon the data availability.
I need to bold the text of the first visible TextView.
I have tried many things but couldn't resolve the issue.
I have tried:
Looping through the child inside the parent layout and
finding the first child and making it bold. This approach always
finds the first child inside the parent layout regardless of it's
visiblility.
I put a check before retrieving the first element if view.visibility
== View.VISIBLE and also I checked view.isShown, both way again returns the first child in the view hierarchy irrespective of the visibility. So, view.visibility always returns View.VISIBLE.
What am I missing and how can I make it work?
Providing sample codes:
XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView1Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Second textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView2Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textView1" />
<TextView
android:id="#+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Third textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView3Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
CODE
binding.apply {
/** We have three textviews.
* textView1 doesn't have data so we are hiding it.
* textView2 has data so, we need to show it.
* and textView2 will be the first item, we need to bold it as well. */
textView1.visibility = View.GONE
/** looping through the children of root layout and bolding the first visible */
rootLayout.forEach exit# { view ->
if(view.isVisible || view.isShown) {
(view as TextView).typeface = Typeface.DEFAULT_BOLD
return#exit
}
}
}
Your code is looping through all the children of the ConstraintLayout because return#exit acts like a continue and not a true break.
Try the following:
run exit#{
rootLayout.forEach { view ->
if (view.isVisible || view.isShown) {
(view as TextView).typeface = Typeface.DEFAULT_BOLD
return#forEach
}
}
}
There may be a more elegant way to go about this, but the above should work.
Update to code:
rootLayout.children.firstOrNull { view ->
view.isVisible || view.isShown
}?.also { view ->
(view as TextView).typeface = Typeface.DEFAULT_BOLD
}
I will also mention that your code will find the first visible child of the ConstraintLayout child order, but that view may not be the view that one would pick as the first looking at the screen. That is because the view order within the children of ConstraintLayout may differ from the on-screen order due to layout constraints. This may not be a problem for you, but it is something to think about.

How to create an android Bottom sheet that fits the size of the sheet with view always aligned to bottom?

I had a bit of trouble figuring out how to word this one when researching so I've made a quick mockup of what I'm trying to achieve below.
my app has a persistent bottom sheet and I would like the contents of the sheet to be resized based on whether the sheet is expanded or half expanded.
My sheet consists of a recycler view and a view at the bottom with some buttons. Currently, the contents of the sheet are always sized as though the sheet is full expanded. This means that the buttons are not displayed unless the sheet is fully expanded and much of the recycler view is also hidden behind the cropped-off sheet. What I want to happen is for the buttons to always be displayed at the bottom of the sheet, even when said sheet is half expanded, and for the recyclerview to fill whatever space is left. Is this possible in a persistent bottom sheet and if now, what means should I use to achieve this? And help would be greatly appreciated.
You can make the buttons layout to always be displayed at the bottom of the sheet by setting its y coordinate by tracking the bottom sheet sliding using addBottomSheetCallback callbacks:
val bottomSheet = findViewById<ConstraintLayout>(R.id.bottom_sheet)
val bottomLayout = findViewById<LinearLayout>(R.id.button_layout)
// initial setting the y coordinates of the bottom layout
bottomSheet.post {
val bottomSheetVisibleHeight = bottomSheet.height - bottomSheet.top
bottomLayout.y =
(bottomSheetVisibleHeight - bottomLayout.height).toFloat()
}
BottomSheetBehavior.from(bottomSheet).apply {
addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
// set the y coordinates of the bottom layout on bottom sheet slide
val bottomSheetVisibleHeight = bottomSheet.height - bottomSheet.top
bottomLayout.y =
(bottomSheetVisibleHeight - bottomLayout.height).toFloat()
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
})
}
Layout:
<?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">
<!-- Main Layout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:behavior_hideable="false"
app:layout_behavior="#string/bottom_sheet_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#CFE2F3"
app:layout_constraintBottom_toTopOf="#+id/button_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/button_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#FF03DAC5"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginEnd="40dp"
android:layout_weight="1"
android:text="cancel" />
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:text="ok" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Android vertical recyclerview with horizontal overflow

I'm trying to implement a document tree using a RecyclerView. Because the tree is expandable, I'm using a custom adapter for it. All of the layout items in the RecyclerView are set to WRAP_CONTENT width, along with the RecyclerView itself. Because the labels in the items can be long and nested, I want the list to be horizontally scrollable to see the clipped items, and the list orientation to be vertical. Comment if you need anything else.
branch.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:padding="10dp"
android:id="#+id/title"
android:gravity="center"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="#+id/arrow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/ic_baseline_keyboard_arrow_down_24"/>
</LinearLayout>
activity
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".TreeActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</HorizontalScrollView>
Binding the view(The adapter is separately managed)
class BranchViewHolder(val branchBinding: BranchBinding) : TreeViewBinder.ViewHolder(branchBinding.root)
override fun getLayoutId(): Int = R.layout.branch
override fun provideViewHolder(itemView: View?): BranchViewHolder = BranchViewHolder(BranchBinding.bind(itemView!!))
override fun bindView(p0: BranchViewHolder?, p1: Int, p2: TreeNode<*>?) {
Log.i(TAG, "bindView: ${p2?.isExpand}")
val rotateDegree = if (p2?.isExpand == true) -90 else 0
p0?.branchBinding?.arrow?.rotation = rotateDegree.toFloat()
with(p2?.content as Branch) {
p0?.branchBinding?.title?.text = this.name
p0?.itemView?.layoutParams= RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
}
Screenshot
Problem - Want to horizontally scroll
Add app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" to your RecyclerView.
For example if you want to scroll the items horizontally, try this one :
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
Don't forget to add the orientation for the vertical / horizontal scroll.

Smoothly animate BottomSheet peek height

I'm trying to animate the height of the BottomSheet on layoutChange, but I can't get anything to work. I've tried several tutorials without luck.
Currently the height just snaps. I'm using a custom BottomSheetDialogFragment(). I've tried TransitionManager.beginDelayedTransition(sheetParent, AutoTransition()) with sheetParent obviously being the top most layout in my setup. It is a coordinatorLayout.
This is my layout file:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/sheetParent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/bottomSheetBehavior"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/bottomSheetContent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="#+id/drag_pill"
android:layout_width="50dp"
android:layout_height="5dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="#drawable/bottom_sheet_pill"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Below the normal view are more views, which I removed to make this a little more readable. I extend and minimize cards in the bottomSheet with the .isVisible = true/false.
How can I smoothly animate that?
You can animate any kind of value that you would like to change in a View using the ValueAnimator.
For example, in your case in order to animate smoothly the peeking height of a bottom sheet, you can use this in your BottomSheetDialogFragment():
Kotlin:
val behavior by lazy { (dialog as BottomSheetDialog?)?.behavior }
val originalHeight = 500 // current height
val updatedHeight = 1000 // desired height
private fun updatePeekHeight(originalHeight: Int, updatedHeight: Int) {
ValueAnimator.ofInt(originalHeight, updatedHeight).apply {
addUpdateListener {
behavior?.peekHeight = it.animatedValue as Int
}
duration = 300 // adjust the duration accordingly
start()
}
}

How to hide footer in RecyclerView Adapter?

I want to add footer with empty layout to the bottom of the recyclerView which will show up only if certain View is visible on screen(In my case FabButton). FabButton is overlapping my last RecyclerView item, so I want to add empty Footer to the bottom of the recyclerview if this FabButton is visible.
ViewHolder inside Adapter for Footer:
inner class FooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
Inside onBindViewHolder:
... ViewHolder above is for items.
else if(holder is FooterViewHolder){
if (parentFragment.isFabButtonVisible()){
holder.itemView.visibility = View.VISIBLE
} else {
holder.itemView.visibility = View.GONE
}
}
This part of the code is working(also else branch is executed correctly), but my footer view is permanently visible on screen. Even if i add only View.GONE it is still visible.
XML for Parent Activity:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#color/splash_white"
android:layout_weight="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
>
<com.app.TabLayoutEntity.CustomViewPager
android:id="#+id/fragmentViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="#color/splash_white"
tools:layout_editor_absoluteY="-2dp" />
<ImageButton
android:id="#+id/fabButton"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:background="#drawable/fab_gradient"
android:clickable="true"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:padding="8dp"
android:elevation="8dp"
android:shape="oval"
android:src="#drawable/ico_fab" />
</android.support.design.widget.CoordinatorLayout>
Fragment XML(Inside ViewPager):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clipToPadding="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/itemRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/recyclerview_corners"
android:clipToPadding="false"
android:scrollbars="vertical"
android:paddingBottom="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
If You just wanna avoid overlapping of last item and Fab then you should do this below simple change in xml on recyclerview and parent layout
<android.support.v7.widget.RecyclerView
android:clipToPadding="false"
android:paddingBottom="paddingindp"/>// for example You can add 100dp and test
and on parent layout add this below line
android:clipToPadding="true"
this will make your recyclerview scroll over that Fab and its doesnt require to add Footer view
Hope this work for your requirement.
You can add your condition in getItemCount() method since you need to increase your list size if you add footer
Like,
override fun getItemCount(): Int {
if (parentFragment.isFabButtonVisible()){
return mModels.size + 1
} else {
return mModels.size
}
}

Categories

Resources