Make width of horizontal recycler view items 70% of screen width - android

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'

Related

How to fill item width children in reyclerview android

Hey I am working in android. I want to fit children in whole view of recyclerview android. I have horizontal recylerview. I will show what I want in diagram.
Scenario 1
when I have more item in recyclerview, I want to show children like this in my recycler view.
Expected Output
Scenario 2
when I have three item I want to show like this. It will fill whole view in reyclerview.
Expected Output
Scenario 3
When I have Two item in reyclerview, I need to look like this
Expected Output
The problem I am getting that I have 3 item, the view is not filling fully. Actually I want to stretch whole view to full width like Scenario 2.
Actual output
item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<LinearLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#drawable/drawable_selector"
android:orientation="vertical">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp" />
<TextView
android:id="#+id/subText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp" />
</LinearLayout>
<RelativeLayout
android:id="#+id/tagContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone">
<TextView
android:id="#+id/tagText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingStart="10dp"
android:paddingEnd="10dp" />
</RelativeLayout>
</FrameLayout>
UPDATE
after #Cheticamp suggestion I did this code
companion object {
fun bindView(parent: ViewGroup): XYZViewHolder {
val view = XyzLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val lp = view.container.layoutParams
lp.width = parent.measuredWidth / 3
return OptionsViewHolder(
view
)
}
}
As you can see my last item is cut from the end.
I think in my framelayout i Used marginEnd 10 dp is it causing issue? please refer my layout if you need more. And one more thing I didn't divide framelayout instead I take linear layout as container. I am adding my github link.
You can change the width of the RecyclerView item views in onCreateViewHolder(). The ViewGroup parent is the RecyclerView.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
val lp = view.layoutParams
// Change the width of the item view here
lp.width = parent.measuredWidth / 3 // width is 1/3 of the width of the RecyclerView
return ItemViewHolder(view)
}
Customize frameLayout class
make a class inherit FrameLayout or another layout you use.
public class SquareFrameLayout extends FrameLayout {
public SquareFrameLayout(#NonNull Context context) {
super(context);
}
public SquareFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public SquareFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SquareFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
onMeasure method is important.
then change root layout item recyclerView to SquareFrameLayout (The class built now)
like this:
<com.example.app.SquareFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/background_rv_item"
android:orientation="vertical"
android:padding="16dp"
android:layout_margin="4dp"
xmlns:tools="http://schemas.android.com/tools">
//your items
</com.example.app.SquareFrameLayout>
You can use flexbox-layout library provided by Google.
You can adjust the items according to your need.
Download the app and see this demo working for you or not. https://github.com/google/flexbox-layout

Minimise width of a text view for a given maximum width in Android

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
}
}

How to get real height layout with layout_height="0dp"?

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)
}
})
}

How to make a GridLayout to change its children views size accordingly?

Background
I need to make a dialpad-like View, like on the Phone app.
I'm using a GridLayout of Views. Each cell is of the same size, and contains just a simple TextView that should change its font size if needed.
The problem
I've succeeded, but for some reason it doesn't work well according to the space that it is given.
If it has a lot of space, it works fine:
However, when it gets smaller (example: small screens, landscape, split-window...), only the top buttons of the grid become visible, and they didn't even change their font size, as if they all want to be of the biggest size they can:
What I've tried
I tried to modify various attributes of the views, but none helped.
I know though, that the dial-pad of the Phone app doesn't really change its font size. Up to some size, it gets shown normally, and if it's too small, it changes to a different layout. This is especially important for landscape and split-window modes.
Here's the code I've made (I change the value of "layout_constraintHeight_percent" to check the various sizes for the top area) :
gradle
...
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
...
QueryKeyboard.kt
class QueryKeyboard #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
init {
orientation = HORIZONTAL
clipChildren = false
clipToPadding = false
columnCount = 3
rowCount = 4
//workaround for a weird issue of seeing just 3 huge buttons, instead of all
val runnable = Runnable {
for (i in 1..9)
addView(generateGridTextButton(i.toString()))
addView(generateGridTextButton("*"))
addView(generateGridTextButton("0"))
addView(generateGridTextButton("+"))
}
if (isInEditMode)
runnable.run()
else
this.doOnPreDraw { runnable.run() }
}
private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): TextView {
val tv = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false) as TextView
tv.text = textToShowAndAddUponClick
return tv
}
}
grid_text_button.xml
<androidx.appcompat.widget.AppCompatTextView
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="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:breakStrategy="balanced"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
android:gravity="center"
android:textColor="#000"
android:textSize="36dp"
app:autoSizeMaxTextSize="36dp"
app:autoSizeMinTextSize="12dp"
app:layout_columnWeight="1"
app:layout_gravity="fill"
app:layout_rowWeight="1"
tools:layout_gravity="center"
tools:targetApi="m"
tools:text="1" />
Usage in activity_main.xml :
<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="match_parent" android:orientation="vertical" tools:context=".MainActivity">
<TextView
android:id="#+id/textView" android:layout_width="match_parent" android:layout_height="0px"
android:background="#33ff0000" android:gravity="center" android:text="some content"
app:layout_constraintBottom_toTopOf="#id/queryKeyboard" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.6" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.sample.myapplication.QueryKeyboard
android:id="#+id/queryKeyboard" android:layout_width="match_parent" android:layout_height="0px"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="#id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
EDIT: I tried to wrap the TextView with FrameLayout, to show the size of each cell:
grid_text_button.xml
<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="wrap_content" android:layout_height="wrap_content"
app:layout_columnWeight="1" app:layout_gravity="fill" app:layout_rowWeight="1">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless" android:breakStrategy="balanced"
android:clickable="true" android:focusable="true" android:focusableInTouchMode="false" android:gravity="center"
android:textColor="#000" android:textSize="36sp" app:autoSizeMaxTextSize="36sp" app:autoSizeMinTextSize="12sp"
tools:layout_gravity="center" tools:targetApi="m" tools:text="1" />
</FrameLayout>
QueryKeyboard.kt
class QueryKeyboard #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
private var cellBackgroundColor = 0xffff0000.toInt()
init {
orientation = HORIZONTAL
clipChildren = false
clipToPadding = false
columnCount = 3
rowCount = 4
//workaround for a weird issue of seeing just 3 huge buttons, instead of all
val runnable = Runnable {
for (i in 1..9) {
addView(generateGridTextButton(i.toString()))
}
addView(generateGridTextButton("*"))
addView(generateGridTextButton("0"))
addView(generateGridTextButton("+"))
}
if (isInEditMode)
runnable.run()
else
this.doOnPreDraw { runnable.run() }
}
private fun switchColor() {
cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
}
private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
switchColor()
view.setBackgroundColor(cellBackgroundColor)
view.textView.text = textToShowAndAddUponClick
return view
}
}
And here are the 2 cases, of when it works fine, and when it doesn't:
Same as before. Getting 3 cells, text not centered, and not auto-resizing its font.
The questions
Why don't the cells adjust their sizes, including the font size of each of them? How come I see just 3 cells when it's too small? How can I fix it?
Is there a better alternative? I guess I could use LinearLayout of multiple LinearLayout instances, but that's just weird for this case... After all, how often do you use GridLayout... :)
How can I detect when it's just too small, so that I switch to another layout, like on the Phone app, including all the various cases they used (even split-window)? Is it possible they just used qualifier for the layouts? If so, which is recommended for this case ?
Why don't the cells adjust their sizes, including the font size of each of them? How can I fix it?
A cell can dynamically adjust its size based on two main constraints: the weight of its parent and the value of its textsize or childview. Having a fixed textsize on cells can lead to inconsistency in design. To fix this issue you can either set a layout weight on parentview with cells width and height matching parents or create different dimensions of textsize for targeted devices.
Is there a better alternative? I guess I could use LinearLayout of multiple LinearLayout instances, but that's just weird for this case... After all, how often do you use GridLayout... :)
There is a better way and that is what you're currently implementing The GridLayout. Using indented LinearLayouts would limit you from lots of benefits and would make you write more code, take for example cases where you need to switch or animate cells, access the nth column of nth row, dynamically change cell span, etc. All these can be done via Gridlayout with a few lines of code. It is more powerful than you think.
How can I detect when it's just too small, so that I switch to another layout, like on the Phone app, including all the various cases they used (even split-window)? Is it possible they just used qualifier for the layouts? If so, which is recommended for this case ?
There's nothing for you to detect, just follow the guidelines and Android would do the detecting.
Here are a couple of ways you can manage your scenario based on Android Design guidelines
First:
Create a layout landscape and portrait mode for your activity (layout/activity.xml and layout-land/activity.xml)
layout/activity.xml
<LinearLayout 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="match_parent"
android:orientation="vertical"
android:weightSum="2"
tools:context=".MainActivity">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#33ff0000"
android:gravity="center" android:text="some content"
android:layout_weight="1" />
<com.sample.myapplication.QueryKeyboard
android:id="#+id/queryKeyboard"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
layout-land/activity.xml
<LinearLayout 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="match_parent"
android:orientation="horizontal"
android:weightSum="2"
tools:context=".MainActivity">
<TextView
android:id="#+id/textView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#33ff0000"
android:gravity="center" android:text="some content"
android:layout_weight="1" />
<com.sample.myapplication.QueryKeyboard
android:id="#+id/queryKeyboard"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Furthermore You need to handle textsize for various screen size.
This textsize calculation can be handled automatically by adding Intuit android library to your dependency list in gradle
dependencies {
implementation 'com.intuit.ssp:ssp-android:1.0.6'
}
Then in your grid button textsize call
android:textSize="#dimens/_30ssp"
OK I've changed the layout files a bit, to avoid the 3-cells issue. It still occurs, but on much smaller sizes. Sadly the font sizes issue remains the same, and even the very small this time.
If anyone finds out why this occurs, please let me know. For now I consider this as a bug, so I've reported here.
grid_text_button.xml
<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="wrap_content"
android:layout_height="wrap_content" android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true" android:focusable="true" android:focusableInTouchMode="false" app:layout_columnWeight="1"
app:layout_gravity="fill" app:layout_rowWeight="1" tools:layout_gravity="center">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:breakStrategy="balanced" android:gravity="center"
android:textColor="#000" app:autoSizeMaxTextSize="36sp" app:autoSizeMinTextSize="12sp" tools:targetApi="m"
tools:text="1" />
</FrameLayout>
QueryKeyboard.kt
class QueryKeyboard #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GridLayout(context, attrs, defStyle) {
// private var cellBackgroundColor = 0xffff0000.toInt()
init {
orientation = HORIZONTAL
clipChildren = false
clipToPadding = false
columnCount = 3
rowCount = 4
for (i in 1..9)
addView(generateGridTextButton(i.toString()))
addView(generateGridTextButton("*"))
addView(generateGridTextButton("0"))
addView(generateGridTextButton("+"))
}
// private fun switchColor() {
// cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
// }
private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
// switchColor()
// view.setBackgroundColor(cellBackgroundColor)
view.textView.text = textToShowAndAddUponClick
return view
}
}
An alternative to the GridLayout implementation, that doesn't have this issue, but it's still weird that I would use it, is as I wrote, a LinearLayout of LinearLayouts :
class QueryKeyboard2 #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
//private var cellBackgroundColor = 0xffff0000.toInt()
init {
orientation = VERTICAL
clipChildren = false
clipToPadding = false
val columnCount = 3
val rowCount = 4
val cellsList = ArrayList<View>()
for (i in 1..9)
cellsList.add(generateGridTextButton(i.toString()))
cellsList.add(generateGridTextButton("*"))
cellsList.add(generateGridTextButton("0"))
cellsList.add(generateGridTextButton("+"))
for (i in 0 until rowCount) {
val rowLayout = generateRowLayout(context)
for (j in 0 until columnCount) {
val cellView = cellsList[i * columnCount + j]
val cellLayoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT)
cellLayoutParams.weight = 1f
rowLayout.addView(cellView, cellLayoutParams)
}
// switchColor()
// rowLayout.setBackgroundColor(cellBackgroundColor)
addView(rowLayout)
}
}
private fun generateRowLayout(context: Context): LinearLayout {
val result = LinearLayout(context)
result.layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0)
(result.layoutParams as LayoutParams).weight = 1f
result.orientation = HORIZONTAL
return result
}
//private fun switchColor() {
// cellBackgroundColor = if (cellBackgroundColor == 0xffff0000.toInt()) 0xff00ff00.toInt() else 0xffff0000.toInt()
//}
private fun generateGridTextButton(textToShowAndAddUponClick: CharSequence): View {
val view = LayoutInflater.from(context).inflate(R.layout.grid_text_button, this, false)
//switchColor()
//view.setBackgroundColor(cellBackgroundColor)
view.textView.text = textToShowAndAddUponClick
return view
}
}
As for trying to make it shown only when there is enough space (height) , I've set the layout to use it on "res/layout-h400dp" (can be changed according to the needs), and a different one, where the dialpad is on the right, for the normal "res/layout".

Apply offset to GridLayout items and preserve equal size among all items

I want to create a space between items in the grid. The items should all have equal width/height.
What I've tried:
Create a GridLayoutOffsetDecorator that applies an offset to all grid items:
class GridLayoutOffsetDecorator(var offset: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State?) {
super.getItemOffsets(outRect, view, parent, state)
outRect.set(offset, offset, offset, offset)
}
}
Having an offset of 8dp creates a 16dp space between the items. So we still have to apply 8dp of padding to the outer edges:
<android.support.v7.widget.RecyclerView
android:id="#+id/productList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="8dp" />
The result is:
Problem: the items are not equal in size:
You notice a slight difference in height when you look at the blue line. This difference appears only after padding the recyclerview. This padding seems to slightly resize some of the items. Do you guys have any experience with this problem? Any idea how this can be solved?
By removing the image from the items, the height difference disappears. This is how the image is set:
<SquareImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:imageResource="#{product.image}" />
SquareImageView:
class SquareImageView : ImageView {
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
}
}
So the problem may be caused by the image itself.
I had the same problem when working with GridLayoutManager. Solved it by setting width and height for image in adapter.
Here's my solution, hope it help.
That's how I placed ImageView in my layout.
<LinearLayout
android:id="#+id/layoutImageProduct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
tools:minHeight="100dp"
tools:minWidth="150dp">
<ImageView
android:id="#+id/imageProduct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="#drawable/placeholder_product"/>
</LinearLayout>
And then apply LayoutParams on parent layout and ImageView
//Setting View width/height
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.layoutImageProduct.getLayoutParams();
params.width = (int) ResourcesUtils.getColumnWidth(context);
params.height = ((int) (ResourcesUtils.getColumnWidth(context)));
holder.layoutImageProduct.setLayoutParams(params);
params = (LinearLayout.LayoutParams) holder.imageProduct.getLayoutParams();
params.width = (int) ResourcesUtils.getColumnWidth(context);
params.height = (int) ResourcesUtils.getColumnWidth(context);
holder.imageProduct.setLayoutParams(params);
Here's the getWidth method.
public static float getColumnWidth(Context context) {
if (GRID_COLUMN_WIDTH == -1) {
int screenWidth = getDisplayMetrics(context).widthPixels;
float horizontalPadding = getDimensInPixel(context, R.dimen.activity_horizontal_margin);
// NUM OF COLUMN = 2
GRID_COLUMN_WIDTH = screenWidth / 2 - horizontalPadding * 2;
}
return GRID_COLUMN_WIDTH;
}
See the screenshot, one with placeholder and one with image.
Can you please try with imageview height as fixed.

Categories

Resources