Make custom view height match constraints in ConstraintLayout - java

I'm building a simple custom view, it's just a basic rectangular container at the moment cause I'm having some issues with the ConstraintLayout.
The main layout setup. The textView is constrained to the parent while the Wire view is constrained to the parent and the top to the bottom of the textView
In the onMeasure method I'm trying to make my view's height to match vertical constraint. What I mean is that I want my view's height to fill the space between the two vertical constraints.
Can't find any code about it online, anyone knows how to do it?

In order to match a constraint you need to set the width/height in zero, this way the view will take the space that the constraint declares for example:
<?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="match_parent">
<TextView
android:id="#+id/mytv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="Your wire view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/mytv" />
</androidx.constraintlayout.widget.ConstraintLayout>
Look at the "wire" view i set the height and 0 and then it takes all the space defined by their constraints.

This is in addition to Nestor Perez's solution above but I will concentrate on the Kotlin code for onMeasure() for resolving the height and width.
First we declare width and height variables and set them to 0.
class CustomRectangle #JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
//width and height variables
private var widthSize = 0
private var heightSize = 0
//PAINT OBJECT
private val paint = Paint().apply {
style = Paint.Style.FILL
}
......}
Inside on measure we use resolveSizeAndState() method to calculate the 2 dimens. We then set the height and width as shown below.
//ON_MEASURE
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val minWidth: Int = paddingLeft + paddingRight + suggestedMinimumWidth
val w: Int = resolveSizeAndState(minWidth, widthMeasureSpec, 1)
val h: Int = resolveSizeAndState(MeasureSpec.getSize(w), heightMeasureSpec, 0)
widthSize = w
heightSize = h
setMeasuredDimension(w, h)
}
Then onDraw() we draw the Custom Rectangle using the resolved height and width.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.Whatever_Color
canvas.drawRect(0f, 0f, widthSize.toFloat(), heightSize.toFloat(), paint)
}
Then as Nestor Perez has explained above we set the width and height to 0dp.
<com.custombutton
android:id="#+id/custom_button"
android:layout_width="0dp"
android:layout_height="0dp" ..../>
Cheers

Related

Shimmer Effect not working on Height wrap_content

I'm trying to implement the shimmer effect with the Height of wrap_content but the images are not loading, I know why it is not loading the images because the imageView has wrap_content and the shimmer also has wrap_content but I want the Height Should be wrap_content and not fixed.
After implementing a fixed height of eg 200dp in shimmer it works but after that images are not loading
I want to make it like Pinterest where the height is adjusted according to the image
XML Files
post_item_container_search.xml
<com.google.android.material.imageview.ShapeableImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imagePostSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="11dp"
android:layout_marginEnd="6dp"
android:contentDescription="#string/todo"
app:shapeAppearanceOverlay="#style/RoundedCorner" />
post_item_containe_shimmer.xml
<com.google.android.material.imageview.ShapeableImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageShimmer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:layout_marginTop="11dp"
android:background="#E7E7E7"
android:contentDescription="#string/todo"
app:shapeAppearanceOverlay="#style/RoundedCorner" />
this how it looks like after adding minHeight to both or in actual imageView
By default, the wrapcontent doesn't have any size so it is 0dp you need to define 50dp or something for the height of shimmer then only you can see the shimmering.
Can refer this blog
or try to use this
post_item_container_search.xml
<com.google.android.material.imageview.ShapeableImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/imagePostSearch"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="fitCenter"
android:layout_marginStart="6dp"
android:layout_marginTop="11dp"
android:layout_marginEnd="6dp"
android:contentDescription="#string/todo"
app:shapeAppearanceOverlay="#style/RoundedCorner" />
After implementing a fixed height of eg 200dp in shimmer it works but after that images are not loading
In that case, you should probably set an absolute width/height for the ImageView first. Later, you can set it back to WARP_CONTENT if you really need to. But first you'll need an estimated/absolute width/height for the view.
int height = bitmap.getHeight();
int width = bitmap.getWidth();
ShapeableImageView siv = findViewById(R.id.imagePostSearch);
ViewGroup.LayoutParams params = siv.getLayoutParams();
params.height = height;
params.width = width;
//To set it back to warp content or match parent:
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
Please keep android:minHeight for the ImageView as it will give you a minimum height and also android:height can be wrap_content so it grows if the image is larger than minHeight.
For example,
<com.google.android.material.imageview.ShapeableImageView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageShimmer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:layout_marginStart="11dp"
android:layout_marginTop="11dp"
android:background="#E7E7E7"
android:contentDescription="#string/todo"
app:shapeAppearanceOverlay="#style/RoundedCorner" />
You can create a dummy view for shimmer with fixed height, make its visibility to visible and show it until the image loads, and once the image is loaded make the dummy view visibility gone.
//XML
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<shimmerView //You can use your own
android:id="#+id/dummyShimmerView"
android:layout_width="100dp"
android:layout_height="100dp"/>
<ImageView
android:id="#+id/postImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
</RelativeLayout>
//dummyShimmerView visibility is visible byDefault
Glide.with(context)
.load(url)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
//TODO: something on exception
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
Log.d(TAG, "OnResourceReady")
dummyShimmerView.visibility = View.GONE
postImageView.visibility = View.VISIBLE
return false
}
})
.into(imgView)

Get view height when part of the view is off screen?

Let's say I have a view structure like
<ConstraintLayout>
<CustomViewLayout>
...
...
</CustomViewLayout>
</ConstraintLayout>
This is simplified but I use the above in a bottom sheet and I sometimes change the height of the ConstraintLayout and the peek height of the bottom sheet depending on my CustomViewLayout height. My problem is that if part of the CustomViewLayout is cut off, that is - it is somewhat outside the screen because the ConstraintLayout isn't high enough - I'm no longer able to get a correct "full height" of it. I always only seem to get the visible part on screen of the view in that case.
So how can I get the full height of a view that is partly off screen?
Thanks!
Edit:
I should add that what I've tried is a globalLayoutListener, as well as a customViewLayout.post {} (and the usual customViewLayout.height of course). But none of these measure the full height when part of the view is outside the screen.
To check my previous answer, I developed a test project to examine the exact situation. It was successful to measure layout height when some part of it is outside of screen (layout height measured 4231px where the screen height was 1920px). If the view is long enough that is not completely shown by the screen, you should put it in a scrollable view. So I put the customViewLayout in a NestedScrollView to consume residual scroll amount after the bottom sheet has expanded. Here is the code and result:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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"
tools:showIn="#layout/activity_main"
tools:context=".MainActivity">
<android.support.constraint.ConstraintLayout
android:id="#+id/layoutBottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/customViewLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/colorAccent">
<TextView
android:id="#+id/stateTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:layout_marginTop="16dp"
android:gravity="center_horizontal"/>
<TextView
android:id="#+id/sizeTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:padding="24dp"
android:gravity="center_horizontal"/>
<TextView
android:id="#+id/contentTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:padding="24dp"
android:text="#string/lorem"
android:gravity="center_horizontal"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>
MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
customViewLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
customViewLayout.viewTreeObserver.removeGlobalOnLayoutListener(this)
val height = customViewLayout.measuredHeight
val width = customViewLayout.measuredWidth
sizeTextView.text = "Height: $height px Width: $width px"
}
})
val sheetBehavior = BottomSheetBehavior.from(layoutBottomSheet)
sheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, offset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> stateTextView.text = "State: Hidden"
BottomSheetBehavior.STATE_EXPANDED -> stateTextView.text = "State: Expanded"
BottomSheetBehavior.STATE_COLLAPSED -> stateTextView.text = "State: Collapsed"
BottomSheetBehavior.STATE_DRAGGING -> stateTextView.text = "State: Dragging"
BottomSheetBehavior.STATE_SETTLING -> stateTextView.text = "State: Settling"
}
}
})
}
}
Screen:
If you are looking for the view width and height, try this:
mCustomViewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mCustomViewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = mCustomViewLayout.getMeasuredHeight()
int width = mCustomViewLayout.getMeasuredWidth()
}
});

Android - view does not keep height

I'm trying to add a custom view to a linearlayout, but the view does not keep it's height. The height is 48dp so it's fixed but after I do addView() it change to wrap_content :
LinearLayout cmsList = (LinearLayout) mView.findViewById(R.id.ll_cms_sections);
View group = getActivity().getLayoutInflater().inflate(R.layout.item_cms_group, null);
cmsList.addView(group);
The group :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="48dp"
android:background="#color/clear_orange">
<com.dekibae.android.popinthecity.presentation.ui.widgets.FontText
android:id="#+id/tv_cms"
style="#style/TitleOrangexNormal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="#dimen/margin_normal"
android:layout_marginStart="#dimen/margin_normal"
android:layout_toLeftOf="#+id/indicator"
android:gravity="center"
android:paddingTop="4dp"
android:text="TEST SECTION"
app:font="#string/plaak" />
<ImageView
android:id="#+id/indicator"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="#dimen/margin_normal"
android:scaleType="centerInside"
android:src="#drawable/dropdown_arrow" />
</RelativeLayout>
The cmsList
<LinearLayout
android:id="#+id/ll_cms_sections"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
I even tried to force it :
group.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 48));
Picking up on #Abtin Gramian's comment the constructor used takes as a dimension pixels, and not dp (density independent pixels)
From the android documentation
android.view.ViewGroup.LayoutParams public LayoutParams(int width,
int height)
Creates a new set of layout parameters with the specified width and height.
Parameters:
width - either WRAP_CONTENT, FILL_PARENT or a fixed size in pixels
height - either
WRAP_CONTENT, FILL_PARENT or a fixed size in pixels
So through the use of
public static float applyDimension (int unit, float value, DisplayMetrics metrics)
we can make a conversion from a dimension in one type of metrics like TypedValue.COMPLEX_UNIT_DIP to another, in this case one adequate for the current display.
group.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, context.getResources().getDisplayMetrics())));

How to increase the height of a view in Android Layout dynamically?

How to extend the height of the separated vertical line (view) dynamically in the image?
This is the XML I'm using:
<View android:layout_width="1dp"
android:layout_height="100dp"
android:id="#+id/view_verdict"
android:background="#color/line"
android:foregroundGravity="center_horizontal"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
Maybe this example code will be useful (I suppose that your view is a LinearLayout):
final int newHeight = 100; //this is the new height will be set in your view
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, newHeight);//first param is the width, and second the height
myView.setLayoutParams(params);
Or just put:
...
android:layout_height="match_parent"
...
set
android:layout_height="match_parent"
for your view with id view_verdict

Custom component layout order

I have a custom component which is overriding the onLayout method as follows:
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
int[] location = new int[2];
this.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
int w = this.getWidth();
int h = y+(8*CELL_HEIGHT);
updateMonthName();
initCells();
CELL_MARGIN_LEFT = 0;
CELL_MARGIN_TOP = monthNameBounds.height();
CELL_WIDTH = w / 7;
setFrame(x, y, x+w, h);
super.onLayout(true, x, y, w, h);
}
However, when using this component in a linearlayout as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TextView above"
android:textSize="15sp"
/>
<com.project.calenderview.CalendarView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TextView below"
android:textSize="15sp"
/>
</LinearLayout>
The layout is rendered such that the textview "TextView above" is first, "TextView below" is second and then my component which is expected to be in the middle between both.
Edit:
Here's also the onDraw:
#Override
protected void onDraw(Canvas canvas) {
// draw background
super.onDraw(canvas);
//Draw month name
canvas.drawText(monthName, (this.getWidth() / 2) - (monthNameBounds.width() / 2), monthNameBounds.height() + 10, monthNamePaint);
//Draw arrows
monthLeft.draw(canvas);
monthRight.draw(canvas);
// draw cells
for(Cell[] week : mCells) {
for(Cell day : week) {
if(day == null) continue;
day.draw(canvas);
}
}
}
What is it that am doing wrong here?
Thanks
the onLayout methods is passing int left, int top, int right, int bottom indicating where your view is supposed to draw, but apparently you're completely ignoring it. You muse use those values to layout it properly.
edit:
if you're not extending a ViewGroup you shouldn't be overriding onLayout(bool, int, int, int, int)
from the docs View.onLayout:
Called from layout when this view should assign a size and position to
each of its children. Derived classes with children should override
this method and call layout on each of their children.
Derived classes with children should override this method and call layout on each of their children
The issue you're having on the final result is in some other part of your code.

Categories