I need to set the height of the FrameLayout android:id="#+id/heart_strength according to the height of the FrameLayout android:id="#+id/heart_strength_background, whose height is set like this:
<FrameLayout
android:id="#+id/cardiogram_background_light"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="#dimen/default_screen_margin"
android:layout_marginTop="#dimen/chart_widget_margin_top"
android:layout_marginEnd="#dimen/default_screen_margin"
android:background="#drawable/chart_widget_background_light_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.184"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/heart_rate_diagram_background_light" />
<FrameLayout
android:id="#+id/heart_strength_background"
android:layout_width="#dimen/cardiogram_status_bar_width"
android:layout_height="0dp"
android:layout_marginEnd="#dimen/default_screen_margin"
android:background="#drawable/chart_widget_background_dark_gray"
app:layout_constraintBottom_toBottomOf="#+id/cardiogram_background_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/cardiogram_background_light" >
<FrameLayout
android:id="#+id/heart_strength"
android:layout_width="match_parent"
android:layout_height="0dp"
app:heartStrength="#{viewmodel.heartStrengthLiveData}"
android:background="#drawable/chart_widget_background_light_gray"
android:backgroundTint="#color/turquoise"
android:layout_gravity="bottom"/>
</FrameLayout>
When I try to get real height of heart_strength.parent layout with:
#JvmStatic
#BindingAdapter("app:heartStrength")
fun setHeartStrengthViewHeight(bar: FrameLayout, level: Int) {
val barParent = bar.parent as FrameLayout
println("bar parent height: ${barParent.layoutParams.height}")
}
I get 0. How can I know the actual height?
I have a card (cardiogram_background_light). Its height changes dynamically in %. Because of this, the maximum height of the bar located in this card also changes dynamically. Previously, I set its height as maxHeight * Value /100. But now maxHeight dynamically changes on different screen sizes and I want to know its value.
It is because the views are not drawn yet when setHeartStrengthViewHeight is called. To solve this, try the following:
#JvmStatic
#BindingAdapter("app:heartStrength")
fun setHeartStrengthViewHeight(bar: FrameLayout, level: Int) {
val barParent = bar.parent as FrameLayout
val observer : ViewTreeObserver = barParent.viewTreeObserver
observer.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
println("bar parent height: ${barParent.height}")
barParent.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
Related
I am building a chat UI.
I want a TextView which:
Is exactly 16dp from the left of the layout
Is at least 150dp from the right of the layout
Has the minimum number of lines that satisfy the above constraints yet still shows all text
Has the minimum width possible for the amount of lines, while still showing all the text
The layout is currently failing on 4).
Here is my current layout XML:
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent" android:layout_marginLeft="16dp"
app:layout_constraintRight_toRightOf="parent" android:layout_marginRight="150dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
android:breakStrategy="balanced"
android:text="This is a message that should wrap tightly" />
</androidx.constraintlayout.widget.ConstraintLayout>
The below is what the layout looks like.
I want to get rid of the extra space to the right of the TextView, as shown by the red arrow. This is important as I plan to put a bubble UI around the TextView and it would look odd if it has this extra space.
Ended up having to implement a custom class to solve this:
class LineBreakTextView(context: Context, attrs: AttributeSet?): androidx.appcompat.widget.AppCompatTextView(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val layout: Layout? = layout
if (layout != null) {
val width = (Math.ceil(getMaxLineWidth(layout).toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight)
val height = measuredHeight
setMeasuredDimension(width, height)
}
}
private fun getMaxLineWidth(layout: Layout): Float {
var maxWidth = 0.0f
val lines: Int = layout.lineCount
for (i in 0 until lines) {
if (layout.getLineWidth(i) > maxWidth) {
maxWidth = layout.getLineWidth(i)
}
}
return maxWidth
}
}
Hey I want to show badge drawable. I tried code from here Does BadgeDrawable not work for views within a FrameLayout such as buttons, images, textviews etc.?, BadgeDrawable does not appear on elements other than BottomNavigationView, Badge Drawable not showing. I successfully show but problem is cutting from side edge.
<FrameLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="24dp"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="6dp"
android:paddingTop="4dp"
android:paddingEnd="9dp"
android:paddingBottom="4dp"
app:layout_constraintBottom_toBottomOf="#+id/xyz"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/xyz">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:src="#drawable/ic_settings"
tools:ignore="ContentDescription" />
</FrameLayout>
activity.kt
fun addBadgeDrawable(count: Int, target: View, parent: FrameLayout, context: Context) {
target.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val badgeDrawable = BadgeDrawable.create(context)
badgeDrawable.number = count
badgeDrawable.badgeGravity = BadgeDrawable.TOP_END
badgeDrawable.setBoundsFor(target, parent)
parent.foreground = badgeDrawable
target.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
private fun BadgeDrawable.setBoundsFor(#NonNull anchor: View, #NonNull parent: FrameLayout) {
val rect = Rect()
parent.getDrawingRect(rect)
this.bounds = rect
this.updateBadgeCoordinates(anchor, parent)
}
I am setting badge view this code line
addBadgeDrawable(10, binding.icon, binding.container, context)
Output
Expected Output
To avoid cutting, you need to increase paddingTop and paddingEnd in FrameLayout.
Or may be need to add additional ConstraintLayout as a parent for FrameLayout, and play with constraints and paddings.
Hope this will help to someone.
I have simple grid layout with 2 spans, and my RecyclerView item layout looks like this:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/categoryCardView"
android:layout_width="170dp"
android:layout_height="100dp"
app:cardBackgroundColor="#color/colorPrimary"
app:cardCornerRadius="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
What I want is basically even space of all sides, so I set Item Decoration like this:
recylcerView.addItemDecoration(SpacingItemDecorator(16))
class SpacingItemDecorator (private val padding: Int): RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
// outRect.right = padding
// outRect.top = padding * 2
outRect.top = padding
outRect.right = padding/2
outRect.left = padding/2
}
}
But as I expected, I'm getting the wrong behavior. The top/bottom is okay, but if for example, I pass 16 padding to SpacingItemDecorator(as I said I've 2 items in a row), the first item will get 8 on the left as expected, but between the item, space will be 16 (8 from the right of the first item, 8 from the left of the second item).
But the weirdest thing is that the second items also get 16 from the right, instead of just 8(right padding)
My questions are:
Why my second item in the row will get 16 padding from the right instead of just 8 as the first item get from the left?
How can I set equal padding to all sizes?
To get 8dp padding for every RecyclerView item You can set RecyclerView padding to 4dp:
<androidx.recyclerview.widget.RecyclerView
android:padding="4dp"
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
/>
Now to Your ItemDecorator add this:
class SpacingItemDecorator (private val padding: Int) : RecyclerView.ItemDecoration()
{
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
)
{
super.getItemOffsets(outRect, view, parent, state)
outRect.top = padding
outRect.bottom = padding
outRect.left = padding
outRect.right = padding
}
}
And now in onCreate (or wherever You set RecylcerView) You can add this item decorator with padding set to 4:
val x = (resources.displayMetrics.density * 4).toInt() //converting dp to pixels
recyclerView.addItemDecoration(SpacingItemDecorator(x)) //setting space between items in RecyclerView
Result:
Just set your cardview width layout to Match parent, with a margin of 8dp for example. And a height with wrap_content. When dealing with recyclerViews. It's better avoid setting a specific measurement for width and height. Trust me, it will be way easier, and look better.
P.s. setting the dimension of a layout inside your activity/fragment has an effect on performance. Its also better to announce/define the UI inside you xml file.
I have an item layout where I display an image, product name, and product image. I must display image in 1:1.5 ration using constraint layout. But when I load a small image, below texts not displaying.
Below is my code of item 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"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/coordinatorLayoutCartRoot"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jackandphantom.circularimageview.RoundedImage
android:id="#+id/imageViewSlider"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#id/tvTitle"
app:layout_constraintDimensionRatio="WH,1:1.4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:rounded_radius="0"
tools:src="#tools:sample/avatars" />
<TextView
android:id="#+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="4dp"
android:text="Fitted crew neck sweater"
android:textColor="#color/colorBlack"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageViewSlider" />
<TextView
android:id="#+id/tvPrice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="4dp"
android:text="$34.00"
android:textColor="#color/colorBlack"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tvTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Output with long image:- https://i.imgur.com/QVnljX6.png
Output with small image:- https://i.imgur.com/0ZwkVwE.png
And if I replace match_parent with wrap_content, app crashes with below error :-
java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)
I have found out that the problem is in inflating view holder.
I had the same issue and solved it changing
ViewBinding.inflate(inflater)
to
ViewBinding.inflate(inflater, parent, false)
While struggling with this problem, I figured it out what's the problem was. My use case was I was using androidx.viewpager2.widget.ViewPager2, and calling normal Views in RecyclerView.
If you notice the error carefully you would see something like this:
java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)
at androidx.viewpager2.widget.ViewPager2$2.onChildViewAttachedToWindow(ViewPager2.java:170)
Second line is the key to main issue. If you open ViewPager2.java you would see
private RecyclerView.OnChildAttachStateChangeListener enforceChildFillListener() {
return new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(#NonNull View view) {
RecyclerView.LayoutParams layoutParams =
(RecyclerView.LayoutParams) view.getLayoutParams();
if (layoutParams.width != LayoutParams.MATCH_PARENT
|| layoutParams.height != LayoutParams.MATCH_PARENT) {
throw new IllegalStateException(
"Pages must fill the whole ViewPager2 (use match_parent)");
}
}
#Override
public void onChildViewDetachedFromWindow(#NonNull View view) {
// nothing
}
};
}
Android is not taking the match_parent assigned in xml to attach views. May be future improvements would be done in next release of ViewPager2.
Anyways, for now to fix it, set layout params as MATCH_PARENT explicity.
view.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
This view is the parent view holding View Pager childs.
In you case it would be androidx.constraintlayout.widget.ConstraintLayout.
view.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
set width and height in your adapter item of ViewPager2 match_parent
Use match_parent to your viewholder layout of ViewPager2
I have the same error, After sometimes I Found the issue in ViewPager2's adapter item, I changed it from wrap_content to match_parent, and it was Fixed.
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/iv_banner"
android:layout_width="match_parent"
android:layout_height="match_parent"/> <!--Change height here to match_parent-->
You should to write like this:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = inflater.inflate(R.layout.yourlayout, parent, false)
view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
return ViewHolder(view)
}
inner class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
}
For me, the issue was pages (i.e. parent/root layout of the adapter) in ViewPager2 were not match_parent hence the error was
java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)
viewpager2 item should have match_parent width and height. but IF you are using View Binding you should inflate layout such as fragments:
ViewBinding.inflate(inflater, parent, false)
i faced the same problem now , your adapter item width and height has to be match_parent
Possible scenario is also if you dont use parent in inflater.
This crash with exception You have:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OnboardViewHolder {
return OnboardViewHolder(parent.context.inflate(
R.layout.view_onboard_item, null, false
))
}
And this works :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OnboardViewHolder {
return OnboardViewHolder(parent.context.inflate(
R.layout.view_onboard_item, parent, false
))
}
I had the same problem.
I used ViewPager2 to display the slider. But its XML item was not MATCH_PARENT.
After that change the problem was resolved.
I also faced the same issue, and the solution is to set the height as match_parent of your item view i.e layout used in adapter class must have height as MATCH_PARENT
set android:layout_width & android:layout_height to match_parent of ViewPager2 & adapter layout
If you are using Data binding, try this on the Adapter class
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): BannerViewHolder {
return BannerViewHolder(
HomeShopsBannerCellBinding.inflate(LayoutInflater.from(parent.context),
parent, //add this line
false), //add this line
mListener,
)
}
Also set Match parent for adapter cell layout and viewpager2 layout
I solve this by using wrap_content in parent view of ViewPager2
Example:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".presentation.home.view.BannerFragment">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/view_pager_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
So it will take the height of the adapter layout.
I was facing this problem because my viewpager2 was in to a FragmentContainerView with layout_height="wrap_content".
So I changed it to: layout_height="match_parent" and solved the problem :)
If you are using the viewpager2 inside the constraint layout just wrap viewpager2 with a relative layout:
<RelativeLayout
android:id="#+id/sub_service_list_view_cont"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/basket"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/view14">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/sub_service_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
I meet the error as you describe.
First,The problem occured in the recyclerview,so it's no matter with vipager2
Second,What is affected is recycleview's viewholder.
Last,Change the viewholder's width and height,make it is all match parent.
Good luck for you.
I also faced the same issue but when I change the item view height to match parent instead of wrap content it work fine.
for fixed error Pages must fill the whole ViewPager2 (use match_parent) :
Set enough height com.jackandphantom.circularimageview.RoundedImage to match_parent
android:layout_height="match_parent"
Viewpager2 by default forces the pages to be MATCH_PARENT, or an exception will be thrown
A solution for that is to do the following before you do anything to the viewpager. This will prevent the viewpager from throwing an exception when the page size is not MATCH_PARENT. Of course you must make your layout wrap_content in order for this to work.
((RecyclerView) yourViewPager2Object.getChildAt(0)).clearOnChildAttachStateChangeListeners();
Further Explanation.
if you go to the viewpager2 code you will find the following, and what we actually do is disabling this behavior.
/**
* A lot of places in code rely on an assumption that the page fills the whole ViewPager2.
*
* TODO(b/70666617) Allow page width different than width/height 100%/100%
*/
private RecyclerView.OnChildAttachStateChangeListener enforceChildFillListener() {
return new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(#NonNull View view) {
RecyclerView.LayoutParams layoutParams =
(RecyclerView.LayoutParams) view.getLayoutParams();
if (layoutParams.width != LayoutParams.MATCH_PARENT
|| layoutParams.height != LayoutParams.MATCH_PARENT) {
throw new IllegalStateException(
"Pages must fill the whole ViewPager2 (use match_parent)");
}
}
#Override
public void onChildViewDetachedFromWindow(#NonNull View view) {
// nothing
}
};
}
Resources:
https://issuetracker.google.com/issues/70666617
https://gist.github.com/safaorhan/1a541af729c7657426138d18b87d5bd4
I faced this problem and I fixed it just by replacing the
android:layout_height="wrap_content" of item of view pager adapter to
android:layout_height="match_parent"
If you are using RecyclerView as main component and show viewpager2 in this you should use this way;
Main Body;
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="270dp" <!-- Set height to main body on here -->
>
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Item Body;
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent" <!-- This one must be match_parent -->
>
<!-- Some components for your item -->
</androidx.constraintlayout.widget.ConstraintLayout>
The layout_width and layout_height of adapter must be match_parent
I am trying to create this effect
I am using a recycler view but my issue, is that each card is 100% of the width of the screen as apposed to 70%.
Here is the xml code for each item
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rowLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/scale_20dp">
<LinearLayout
android:id="#+id/button_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/currentYear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#drawable/paymentscreengrey"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="35dp"
android:paddingTop="#dimen/scale_50dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/scale_20dp"
android:text="**** **** **** 5432"
android:textColor="#color/white"
android:textSize="#dimen/scale_20dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2345"
android:textColor="#color/white"
android:textSize="#dimen/scale_16dp" />
If you want the first item to be left-aligned (i.e., reproduce what's in the screenshot), subclass LinearLayoutManager and override the three generate*LayoutParams methods. Here's how I did it: https://gist.github.com/bolot/6f1838d29d5b8a87b5fcadbeb53fb6f0.
class PeekingLinearLayoutManager : LinearLayoutManager {
#JvmOverloads
constructor(context: Context?, #RecyclerView.Orientation orientation: Int = RecyclerView.VERTICAL, reverseLayout: Boolean = false) : super(context, orientation, reverseLayout)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
override fun generateDefaultLayoutParams() =
scaledLayoutParams(super.generateDefaultLayoutParams())
override fun generateLayoutParams(lp: ViewGroup.LayoutParams?) =
scaledLayoutParams(super.generateLayoutParams(lp))
override fun generateLayoutParams(c: Context?, attrs: AttributeSet?) =
scaledLayoutParams(super.generateLayoutParams(c, attrs))
private fun scaledLayoutParams(layoutParams: RecyclerView.LayoutParams) =
layoutParams.apply {
when(orientation) {
HORIZONTAL -> width = (horizontalSpace * ratio).toInt()
VERTICAL -> height = (verticalSpace * ratio).toInt()
}
}
private val horizontalSpace get() = width - paddingStart - paddingEnd
private val verticalSpace get() = height - paddingTop - paddingBottom
private val ratio = 0.9f // change to 0.7f for 70%
}
This solution is based on/inspired by the spanning (i.e., fit all items to screen) linear layout manager https://gist.github.com/heinrichreimer/39f9d2f9023a184d96f8.
Btw, if you just want to show the items to the left and to the right, while the current item is centered, you can add padding to the recycler view and set clipToPadding to false. In this case you don't even need a custom layout manager.
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
I needed to have the items in my horizontal RecyclerView to be 70% of the width of the recyclerview. It can be easily done in onCreateViewHolder():
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row, parent, false);
view.layoutParams = ViewGroup.LayoutParams((parent.width * 0.7).toInt(),ViewGroup.LayoutParams.MATCH_PARENT)
return ViewHolder(view);
}
Two ways of doing this really.
1)Use a custom view for your the recycled view. Override onMeasure to return its width as 70 percent of the screen.
2)In your Recycler View adapter, when you create the view set its width to be 70 percent of the screen's width.
In either case you get the screen size from the Display and just multiply the width by .7. In the first case you set that as the EXACT measured width, in the second you set it in the layout param. The second is probably a bit easier.
I had a similar issue where I had a fragment with a horizontal RecyclerView and wanted each view item's width to be a third of the user's screen. Solved it by adding the following in the constructor of our ViewHolder class (in my case I wanted each view holder to be 1/3 of the screen instead of 70%):
private class OurViewHolder extends RecyclerView.ViewHolder {
...
public OurViewHolder (LayoutInflater inflater, ViewGroup parent) {
super (inflater.inflate (R.layout.list_item_layout, parent, false));
// This code is used to get the screen dimensions of the user's device
DisplayMetrics displayMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
// Set the ViewHolder width to be a third of the screen size, and height to wrap content
itemView.setLayoutParams(new RecyclerView.LayoutParams(width/3, RecyclerView.LayoutParams.WRAP_CONTENT));
...
}
...
for your row.xml parent, you must use wrap_content for its width then add this property.
android:paddingLeft="25dp"
You will get same result
Just updated a bit from this answer by adding a margin and getting displayMetrics from the resources to make it clean:
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(product: StyleMaster) = with(itemView) {
val width = context.resources.displayMetrics?.widthPixels
if (width != null) {
val params = RecyclerView.LayoutParams(width / 2, RecyclerView.LayoutParams.WRAP_CONTENT)
params.setMargins(2, 2, 2, 2)
itemView.layoutParams = params
}
// Rest of the code goes here...
Try changing LinearLayout with PercentRelativeLayout to "wrap" the RecyclerView and then set the its width to 70%.
Change this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rv"
/>
</LinearLayout>
With this:
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_widthPercent="70%"
android:id="#+id/rv"
/>
EDIT
Since Percent Support Library comes along with Android Support Library 23 so please make sure that you update Android Support Library in SDK Manager to the latest version already (actually 24). And then add a dependency like below in build.gradle file:
compile 'com.android.support:percent:24.0.0'