I am trying to build a custom view but there seems to be something that I am not getting.
I have overriden the onSizeChanged and onDraw methods and added my custom view in the activity layout and given it a height of say 100dp and pinned it to the bottom and start and end of the parent relative layout.
However the view doesn't render correctly and there is a blank empty space beneath it.
below is my onDraw and onSizedChanged methods
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
viewHeight = h;
viewWidth = w;
}
#Override
protected void onDraw(Canvas canvas) {
// draw background
painter.setStrokeWidth(viewHeight);
painter.setColor(Color.BLUE);
canvas.drawLine(0, 0, viewWidth, 0, painter);
}
below is how I am adding the view to the layout xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_registration"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.activities.RegistrationActivity">
<com.example.custom.widgets.MyCustomView
android:id="#+id/holder"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<FrameLayout
android:layout_above="#+id/holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="kjdgfjkasdgfjkahsdgfjkhsa"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
this is how it looks
FWIW, I also trying embedding my custom view inside a FrameLayout and explicitly setting the height of the FrameLayout and setting the height of my custom view to match_parent. still no success.
Overriding onMeasure() in your custom view class will set the size of your view dependent on the layout constraints provided by the parent.
This should give your custom view a height of 100dp:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = Math.min(100, MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(width, height);
}
If you change the parent's constraints, you will need to change the MeasureSpec values that are passed into the method. See this question: https://stackoverflow.com/a/12267248/7395923
Change the onDraw() method to this:
protected void onDraw(Canvas canvas) {
// draw background
painter.setStrokeWidth(getWidth());
painter.setColor(Color.BLUE);
canvas.drawLine(0, 0, getWidth(), 0, painter);
}
And remove the onSizeChanged() method.
Related
I'm writing a code for a school project where one has to load a data file (CSV file, text file, etc) and from the obtained data, the app will pass the data to a custom draw View and the onDraw method will draw/plot a graph based on the data.
My goal is for the app to display 2 graphs, one after the other (stacked). The first set of data is loaded and the 1st graph is drawn. The loaded data is then used for a different calculation in a different method. The custom draw View is then called again with the new data to draw the 2nd graph.
When I run the app, both charts are drawn but because the x and y-axis' of the graph are coded to be drawn at certain fixed pixels, the 2nd graph is drawn over the first one and therefore only the 2nd graph is visible.
Is there any way I can draw the 2 graphs so that it does not overlap and instead appears to be stacked in ScrollView?
My code is shown below but I've gotten rid of calculations that I think aren't very important. Any help and pointers would be very much appreciated!
MainActivity.java:
#Override
protected void onActivityResult(......) {
super.onActivityResult(......);
switch (1) {
case 1:
Graph graph = this.findViewById(R.id.graph1);
graph.setData(data); // the loaded data is passed to Graph View
Graph drawGraph2 = this.findViewById(R.id.graph2);
graph2.setData(this.newCalculate(data));
break;
}
}
Graph.java
public class Graph extends View {
private Paint mPaint = new Paint();
private final int zero = 700; // mark the 0 line of graph at 700 pixels
public void setData(data){
......
}
public Graph(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
plotUnit(canvas); // plot points on graph
axisLabel(canvas); // label axis
axisLine(canvas); // draw axis
xyAxisMarker(canvas); // mark axis
}
private void plotUnit(Canvas canvas) {
......
// Due to data having negative values, the graph is inverted and the 0 starts
// of the graph is defined at 700 pixels (private final int zero)
}
private void axisLabel(Canvas canvas) {
......
}
private void axisLine(Canvas canvas, int inset) {
......
}
private void xyAxisMarker(Canvas canvas) {
......
}
Update
activity_main.xml
<?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"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/loadbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open Data File" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph1"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph2"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
</LinearLayout>
You cannot have two views' heights match parent height inside of a LinearLayout with vertical orientation. It is not possible because heights of these views must be equal to the parent height but at the same time, they must be ordered one after the other resulting in double of parent's height.
If you imagine parent's height as 10dp then each of the Graph views must be 10dp as well which means parent's height must be 20dp, not 10dp. That is going to cycle forever so the Android does a simple thing: views that are going below the first child view with android:layout_height="match_parent" will have height 0dp or if their height is fixed they will be drawn outside of the layout and will not be visible.
Example
Screenshot from Design tab of layout editor in Android Studio IDE.
Here you can see:
red view as a parent linear layout;
purple view as a first child with height matching it's parent height;
outlined view that is drawn outside of the layout because it is pushed out by the first child with android:layout_height="match_parent";
there is one more view that is crushed to 0 height and thus not visible. You can see it down in the XML code.
XML code of this sample:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#android:color/holo_red_light">
<LinearLayout
android:background="#color/colorPrimary"
android:layout_width="60dp"
android:layout_height="match_parent" <!-- this view's height is a problem -->
android:orientation="vertical"/>
<LinearLayout
android:background="#android:color/holo_green_dark"
android:layout_width="120dp"
android:layout_height="match_parent" <!-- height is not fixed, then it will be 0 -->
android:orientation="vertical"/>
<LinearLayout
android:background="#android:color/holo_green_light"
android:layout_width="120dp"
android:layout_height="40dp" <!-- height is fixed, it is outlined outside of a layout -->
android:orientation="vertical"/>
</LinearLayout>
How to fix the issue?
Set fixed height. As a test try to define a fixed height, e.g. 100dp;
Redesign your layout. Use RelativeLayout or ConstraintLayout to position views relative to each other so that they are always visible no matter what the screen size, ratio, density is.
Example of how to fix
I personally prefer ConstraintLayout as it is very powerful in terms of positioning and adaptation.
<?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">
<Button
android:id="#+id/loadbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open Data File"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph1"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/graph2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/loadbutton" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph2"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/graph1" />
</androidx.constraintlayout.widget.ConstraintLayout>
The result is (I used two buttons instead of Graph views):
Hints:
If you want to use ScrollView then setting fixed height or defining height at runtime will be required.
Get rid of private final int zero = 700; // mark the 0 line of graph at 700 pixels. Do not use pixel values directly as it will lead to error-prone UI. It will be the case of "work on my phone, does not work the other". Use view's height as the 0 line.
I have a custom ConstraintLayout class (Card.java) which overrides the onDraw() method to draw a hexagon in his background. On the foreground i try to have three TextViews to display three numbers.
For this I inflate a card.xml in the constructor of Card. The TextViews are displayed, but not at the right position. They should match the width and height of the Card and then position itself to the top-left and top-right corner and one to the bottom of the Card. But they do something like shrink itself and go to the top-left corner.
I have tried to change the root element of card.xml to "merge" instead of "...ConstraintLayout" but this doesn't change anything.
I also tried to use Guidelines to position the TextViews relative to its width. I try to prevent the use of fixed margins, so the Text is always at the right place, also when the size of the Card changes.
Card.java:
public class Card extends ConstraintLayout {
private int mSize;
private Path mHexagon;
private Paint mPaintHexagon;
private TextView mT1, mT2, mT3;
public Card(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.card, this);
// Numbers
mT1 = findViewById(R.id.num1);
mT2 = findViewById(R.id.num2);
mT3 = findViewById(R.id.num3);
// Hexagon
mSize = Field.getHexSize(); // Size is used to calculate the
setPath();
mPaintHexagon = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintHexagon.setColor(0x50888888);
mPaintHexagon.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mHexagon, mPaintHexagon);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = 2 * mSize;
int height = (int) (Math.sqrt(3) * mSize);
Log.d(TAG, "Measure w:" + width + " h:" + height);
setMeasuredDimension(width, height);
}
}
card.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num2"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="2"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num1"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="1"
app:layout_constraintStart_toEndOf="#+id/num2"
app:layout_constraintEnd_toStartOf="#+id/num3"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/num3"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:text="3"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"/>
</android.support.constraint.ConstraintLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
tools:context=".MainActivity"
android:background="#color/colorAccentDark"
android:padding="5dp">
<de.portugall.markus.takeiteasy.Card
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="#+id/card"/>
</android.support.constraint.ConstraintLayout>
Screenshot Card in Layout-Debug mode
'onMeasure()` has some rules that you are not strictly following. I have seen these rules broken with impunity, but I think that you are being caught, but we will push on.
In onMeasure() you are setting the height and width of the custom layout but ConstraintLayout still understands the layout as wrap_content. You will need to set the layout params to the new height and width. Add the following code to the end of onMeasure():
// Although we have measured the layout, we need to tell ConstraintLayout in the
// LayoutParams that the size is not longer "wrap_content".
ViewGroup.LayoutParams lp = getLayoutParams();
lp.width = width;
lp.height = height;
setLayoutParams(lp);
The second issue that you have is that you are adding a ConstraintLayout (card.xml) to a ConstraintLayout (your custom layout), but you are not setting the constraints. In the constructor for Card.java, add the following to set the constraints:
ConstraintLayout layout = (ConstraintLayout) inflate(context, R.layout.card, this);
// We have added R.layout.card to a ConstraintLayout (this custom layout), so we need
// to make sure that it is constrained properly.
ConstraintSet cs = new ConstraintSet();
cs.clone(layout);
cs.connect(R.id.layout, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
cs.connect(R.id.layout, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
cs.connect(R.id.layout, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END);
cs.connect(R.id.layout, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM);
cs.applyTo(layout);
You will need to change the height and width of the layout in card.xml to 0dp. match_parent is never appropriate in ConstraintLayout.
This is a pictorial description of what is happening:
On a related note, you should consider using the merge facility to avoid nested ConstraintLayouts as other have mentioned.
I have an imageview and I would like to load an image with Glide into it. The thing here is, I want all the images have the same height (width match_parent) according to the 16:9 aspect ratio.
<ImageView
android:id="#+id/thumbImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="#string/thumbnail_text"
android:scaleType="fitCenter"
android:src="#drawable/thumbnail_default"
android:cropToPadding="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/thumbTitle"/>
Glide code
Glide.with(mThumb.getContext())
.load(getThumbUrl())
.into(mThumb);
When I load an image with aspect ratio 16:9, everything is great. However, when I load another image, the height adjusts to the height of the image.
I tried adding Constraint layout dimension ratio
app:layout_constraintDimensionRatio="16:9"
I tried playing around with adjustViewBounds and scaleType but no success.
So I think I should play with Glide to adjust the bitmap before loading it to the imageview but I couldn't find a tutorial about that.
How can I show image with width match_parent and height calculated as aspect ratio 16:9?
Thank you,
Note: I tried
<ImageView
android:id="#+id/thumbImage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:contentDescription="#string/thumbnail_text"
android:scaleType="centerCrop"
android:src="#drawable/thumbnail_default"
android:cropToPadding="true"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/thumbTitle"/>
It works, but then if the orientation is changed, the height goes 0.
One way around is to use a Image view with 16:9 ratio .
public class CustomImageView extends AppCompatImageView {
public CustomImageView(Context context) {
super(context);
}
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height=(width * 9) / 16;
setMeasuredDimension(width, height);
}
}
This will make a ImageView with hard code ratio of 16/9 . You can use Custom attributes for it to make it more flexible.
Update:- Now with the ConstraintsLayout its easy to break views in ratio . U can try following.
<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/expandableModes"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Another way is to use a SimpleTarget<> as describe in this post.
Then you can resize the image based on the current image's size.
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>{
#Override
public void onResourceReady(Bitmap resource,GlideAnimation<? extends Bitmap>(){
// resize the bitmap
bitmap = resize(width,height);
// set the image to the imageView
imageView.setImageBitmap(bitmap);
}
})
I have a ViewPager implemented without fragment (just view). Each page contains a list of elements, but the list hasn't always the same size.
I tried to set the ViewPager dimension with this code (extends ViewPager so I can override this function) :
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
The problem is that it set the same height for every page. I need a custom height for each page, depending of the height of the list.
I also tried to use setOnPageChangeListener calling requestLayout() and invalidate() on the ViewPager, but the size remains the same. Maybe I did it wrong...
Here is some code that might be useful :
A part of my PagerAdapter:
#Override
public Object instantiateItem(ViewGroup container, int position) {
// Inflate a new layout from our resources
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_home_pager,
container, false);
mContainerView = (ViewGroup) view.findViewById(R.id.news_container);
reloadNews(position);
// Add the newly created View to the ViewPager
container.addView(view);
// Return the View
return view;
}
A part of my XML layout:
<ScrollView
android:id="#+id/news_scrollview"
android:layout_width="match_parent"
android:layout_height="fill_parent">
<com.ViewPagerWithHeight
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"/>
</ScrollView>
Thanks of lot for helping me,
The ViewPager height cannot vary per-page as you are hoping. You could attempt to subclass ViewPager to add this ability, but handling the visual transitions between differently-sized pages is going to be very fiddly. Another alternative may be to re-measure the ViewPager on every page change (but I would expect the transition to look pretty bad). You would need to set a page change listener, then call requestLayout on the ViewPager. Your ViewPager onMeasure would need to find the height of the currently-visible child only, and set the ViewPager height to match this.
Aside: I don't see you calling setMeasuredDimension anywhere. When measuring a view, you must call this method with your calculated width and height to inform the view what size it has been measured at. Currently you are calling through to super.onMeasure at the end of your custom onMeasure which, while valid, means the ViewPager will always be measured with the "default" width and height. In this case, that happens to match the height of the largest page anyway.
See the docs for onMeasure for more details. In particular:
CONTRACT: When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an IllegalStateException, thrown by measure(int, int). Calling the superclass' onMeasure(int, int) is a valid use.
I just find out something that do the job.
I moved the Scrollview inside the pageItem.xml so I can set the page size (for all pages) but still can scroll if the list is long enough. The ViewPager is now inside a FrameLayout with layout_height = fill_parent.
The page size is fixed, but the scrollview do the trick...
The ViewPager (inside my fragment) :
<FrameLayout
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/viewpagercontainer">
<com.ViewPagerWithHeight
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"/>
</FrameLayout>
The "Page item" :
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/news_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:showDividers="middle"
android:divider="?android:dividerHorizontal" />
</ScrollView>
onMeasure function:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(containerHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
How to set the attribute containerHeight (more info here):
final View pagerContainer = (View) mView.findViewById(R.id.viewpagercontainer);
pagerContainer.post(new Runnable() {
#Override
public void run() {
mViewPager.setContainerHeight(pagerContainer.getMeasuredHeight());
}
});
In android, how can you create a scroll view that's got a max height, and wrap contents, basically it wraps the content vertically, but has a maximum height?
I tried
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="200dp"
android:layout_alignParentBottom="true" >
<LinearLayout
android:id="#+id/maincontainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
But this isn't working?
you can add this to any view (override onMeasure in a class inherited from a view)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (maxHeight > 0){
int hSize = MeasureSpec.getSize(heightMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
switch (hMode){
case MeasureSpec.AT_MOST:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.AT_MOST);
break;
case MeasureSpec.UNSPECIFIED:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
break;
case MeasureSpec.EXACTLY:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(hSize, maxHeight), MeasureSpec.EXACTLY);
break;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
I've extended ScrollView and added code to implement this feature:
https://gist.github.com/JMPergar/439aaa3249fa184c7c0c
I hope that be useful.
You can do it programmatically.
private static class OnViewGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private final static int maxHeight = 130;
private View view;
public OnViewGlobalLayoutListener(View view) {
this.view = view;
}
#Override
public void onGlobalLayout() {
if (view.getHeight() > maxHeight)
view.getLayoutParams().height = maxHeight;
}
}
And add listener to the view:
view.getViewTreeObserver()
.addOnGlobalLayoutListener(new OnViewGlobalLayoutListener(view));
Listener will call method onGlobalLayout(), when view height will be changed.
It can be done by wrapping the view into ConstraintLayout and using layout_constraintHeight_max attribute.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_max="wrap"
app:layout_constraintVertical_bias="0">
...
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
In the example above, the parent ConstraintLayout height is limited to 200dp, and the child ScrollView height wraps the content till it's less than 200dp. Note that app:layout_constraintVertical_bias="0" aligns the child ScrollView at the top of the parent, otherwise it will be centered.
for set scrollview height you must use 2 linerlayout inside together and then set scrool view as them child then set middle linerlayout layout:height for limit scrollview height.
1.) Create a class to handle setting maximum height to what is passed by the user:
public class OnViewGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private Context context;
private int maxHeight;
private View view;
public OnViewGlobalLayoutListener(View view, int maxHeight, Context context) {
this.context = context;
this.view = view;
this.maxHeight = dpToPx(maxHeight);
}
#Override
public void onGlobalLayout() {
if (view.getHeight() > maxHeight) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = maxHeight;
view.setLayoutParams(params);
}
}
public int pxToDp(int px) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int dp = Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return dp;
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
}
2.) Attach this to the view and pass the maximum height in DP:
messageBody.getViewTreeObserver()
.addOnGlobalLayoutListener(
new OnViewGlobalLayoutListener(messageBody, 256, context)
);
Thanks to #harmashalex for the inspiration. I made modifications to as setting the layout params didn't work by #harma's code. Also, dp-to-px conversion is necessary to offload wondering about it.
Here you can set height of your Scrollview like this -:
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentBottom="true" >
<LinearLayout
android:id="#+id/maincontainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>