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.
Related
Here is my Persistent BottomSheet
<androidx.constraintlayout.widget.ConstraintLayout 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/bs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bottom_sheet_background"
android:elevation="2dp"
android:padding="#dimen/base_margin"
app:behavior_hideable="true"
app:behavior_peekHeight="#dimen/bottom_sheet_peek_height"
app:layout_behavior="#string/bottom_sheet_behavior">
When User scrolls RecycleView, BottomNavigation hides and I reduce height of BottomSheet accordingly in RecycleView's addOnScrollListener using:
binding.rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
//148 = 80(bottom navigation) + 56(bottom sheet)
if (dy < 0)//scroll down
bottomSheetBehavior.setPeekHeight(136, true);
else if (dy > 0)//scroll up
bottomSheetBehavior.setPeekHeight(56, true);
}
After BottomNavigation is hidden and BottomSheet height is reduced, if BottomSheet is clickable,
(either through code binding.bs.bs.setClickable(false); or through xml android:clickable="true")
I can't drag it to expand. If it is not clickable, click event goes through it and user click on RecycleView item underneath it.
Even when its height is not reduced and it isn't clickable then also click event goes under it and fire on RecycleView item.
I also tried setting nestedScrolling, which allowed expanding but after that start creating issues when collapsing. :(
UPDATE:
I noticed BottomSheet drag not works when I set Bottomsheet clickable and its peekheight < 80 dp, ie the height of BottomNavigation.
Reference:
Why am I able to click "behind" the bottomsheet in Android?
I had same use case of having a recycler view inside a bottom sheet and here's what my Bottom sheet XML looks like. Please try to check if this works for you!
Apart from this, I did not actually get it why you want to reduce the height of the bottom sheet, as Bottom sheet has the behavior of self adjusting its height respect to the content inside it.
Please if possible share your use case so that it will be easier for the community to answer which gonna hit the bonsai. Thanks!
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:background="#color/transparent"
android:layout_height="match_parent"
android:paddingTop="50dp"
android:id="#+id/rootLayout"
>
<include
android:id="#+id/progress"
layout="#layout/item_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/bottom_sheet_rounded_background"
android:backgroundTint="#color/background_gray"
android:clipToPadding="true"
android:orientation="vertical"
app:layout_behavior="#string/bottom_sheet_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<RelativeLayout
android:id="#+id/title_rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="24dp"
android:height="24sp"
android:fontFamily="#font/silka_bold"
android:text="#string/error_select_prescription"
android:textColor="#color/text_color_semi_black"
android:textSize="16sp" />
<ImageView
android:id="#+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:src="#drawable/ic_close_btn_gray" />
</RelativeLayout>
<View
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/btn_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/title_rl" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewPrescription"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingTop="#dimen/dimen_16dp"
tools:itemCount="10" />
</androidx.core.widget.NestedScrollView>
<include
android:id="#+id/btn_select_and_proceed"
layout="#layout/sticky_footer_design_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
val bottomSheet = dialog!!.findViewById<View>(R.id.design_bottom_sheet) as FrameLayout
val behavior: BottomSheetBehavior<*> = BottomSheetBehavior.from<View>(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
These all are the states which you can use to manipulate the height or basically the behavior of the BottomSheet.
I have two RecyclerView, both have a vertical orientation, I need to scroll one of them so that the second scrolls, that is, their scrolling is synchronous, I thought that it is possible to apply one LinearLayoutManager to these two RecyclerView and then it will be work, but in this log, the error LinearLayoutManager is already attached to a RecyclerView will be generated, so I don't know how to be, help me find a solution, I need two independent RecyclerView with different adapters, but which scroll synchronously, so do not write about GridLayoutManager, thanks.
xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_0"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:clipToPadding="false"
android:orientation="vertical"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:clipToPadding="false"
android:orientation="vertical"/>
</LinearLayout>
Cod
val RLM_0 = LinearLayoutManager(context)
rv_0.setHasFixedSize(false)
rv_0.isNestedScrollingEnabled = false
rv_0.layoutManager = RLM_0
adapter_0 = Adapter_0(itemTasks, requireActivity())
rv_0.adapter = adapter_0
val RLM_1 = LinearLayoutManager(context)
rv_1.setHasFixedSize(false)
rv_1.isNestedScrollingEnabled = false
rv_1.layoutManager = RLM_1
adapter_1 = Adapter_1(itemTasks, requireActivity())
rv_1.adapter = adapter_1
Solution 1:
Check is: Sync scrolling of multiple RecyclerViews
Solution 2:
There is an other way to do so, the idea is to disable scroll for each recycler view and surround them with a scroll view.
This is how you can implement it:
binding.recyclerViewOne.setOnTouchListener((v, event) -> true);
binding.recyclerViewTwo.setOnTouchListener((v, event) -> true);
And for the layout:
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
</ScrollView>
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.
I'm trying to present a CardView with 3 textviews and a imageView inside a RecyclerView. Problem is that if before you start scrolling imageView is filled completely in cardview and the textviews are hidden even though the relative layout inside the cardview is supposed to keep the image on the left side. After scrolling some of the cardviews will end up looking how i want them to, if i sort the List and update the adapter all the cardviews except the last work like i want them to.
Here's the relevant XML's:
Cardview:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="4dp">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cv"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="1dp"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/cv_photo"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="1dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/cv_name"
android:layout_toEndOf="#+id/cv_photo"
android:layout_alignParentTop="true"
android:textSize="14sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/cv_number"
android:layout_toEndOf="#+id/cv_photo"
android:layout_below="#+id/cv_name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/cv_date"
android:layout_toEndOf="#+id/cv_photo"
android:layout_below="#+id/cv_number"
/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
RecyclerView:
<android.support.v7.widget.RecyclerView
android:id="#+id/ProfileRecycler"
android:layout_width="368dp"
android:layout_height="491dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_below="#+id/viewbox"
android:clipToPadding="false"
/>
Adapter code (has everything that's necessary)
#Override
public ProfileViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){
View view = LayoutInflater.from(segruppe.getContext()).inflate(R.layout.profileview,viewGroup,false);
ProfileViewHolder holder = new ProfileViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(ProfileViewHolder ProfileViewHolder,int i){
profileViewHolder.name.setText(profiles.get(i).getName());
profileViewHolder.number.setText(String.valueOf(profiles.get(i).getNumber()));
profileViewHolder.dato.setText(profiles.get(i).getDate().toString());
if(profiles.get(i).getImage()!=null)
Glide.with(context).load(profiles.get(i).getImage()).into(profileViewHolder.image);
}
Fragment code for creating the cardview recycler:
profileRecycler = getView().findViewById(R.id.ProfileRecycler);
profileAdapter = new ProfileAdapter(getContext(),profileList);
GridLayoutManager glm = new GridLayoutManager(getActivity(),4, LinearLayoutManager.HORIZONTAL,false);
profileRecycler.setLayoutManager(glm);
profileRecycler.setAdapter(profileAdapter);
Can anyone see what is going wrong here? (Might be worth mentioning that if there's no imageView in the cardview then it remains completely static during and after scrolling like it's supposed to)
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
}
}