I would like to have a square (same width as height) GridView fill the full height of the screen in landscape orientation. The Gridview is a chessboard (8 by 8 squares) with the xml:
<com.example.jens.jchess2.view.MyGridView
android:id="#+id/chessboard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp"
android:numColumns="8"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp">
</com.example.jens.jchess2.view.MyGridView>
and the elements of the grid are:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/square"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#000080"
android:orientation="vertical"
android:padding="0pt">
<ImageView
android:id="#+id/square_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:padding="0pt" />
<ImageView
android:id="#+id/piece"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:padding="0pt" />
</FrameLayout>
, where the ImageViews correspond to the squares and pieces (both from png images) of the board.
In the custom MyGridView I override onMeasure as follows:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = getContext().getResources().getDisplayMetrics().heightPixels;
if (width > height) {
super.onMeasure(
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
} else {
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
);
}
}
which gives me a square GridView in both portrait and landscape orientation. In portrait mode it fills the full width and everything is fine. In landscape mode however it extends below the screen because the height (=width) of the GridView/board is too large. It is too large by the height of the toolbar and the height of the statusbar. How can I get the proper size for the GridView, i.e. screen height minus status bar height minus toolbar height?
Start with two versions of your layout file:
/res/layout/grid.xml
...
<!-- full width -->
<com.example.MyGridView
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
/>
...
/res/layout-land/grid.xml
...
<!-- full height -->
<com.example.MyGridView
android:layout_width="wrap_content"
android:layout_height="match_parent"
...
/>
...
You probably already have something like this.
Now in your onMeasure() override, the match_parent dimension will have a MeasureSpec mode of EXACTLY and the wrap_content dimension will have a MeasureSpec mode of AT_MOST. You can use this to achieve your desired layout.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.AT_MOST) {
// portrait
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
} else if (heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.AT_MOST) {
// landscape
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
EDIT: I found out that both modes can be AT_MOST depending on the ViewGroup container. Please see my other answer for updated measuring code.
Ah. Now I see that this is for a game.
Sometimes it's better to have layouts and child views, but in most cases with game boards you are better off creating a single View subclass that represents the game view.
For instance, what if your users say they want the ability to pinch-zoom into one quadrant of the game board? You can't do that with a GridView.
I whipped up a simple app to show you how this can work. I simplified the onMeasure() code I posted before, and instead of a GridView, a single View subclass renders the game board.
The MainActivity simply sets up the content view.
/res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.gameboard.MainActivity">
<com.example.gameboard.GameBoardView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
/res/layout-land/activity_main.xml:
Notice match_parent and wrap_content are switched for width and height.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.gameboard.MainActivity">
<com.example.gameboard.GameBoardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
</RelativeLayout>
GameBoardView.java:
public class GameBoardView extends View {
private Paint mPaint;
public GameBoardView(Context context) {
super(context);
}
public GameBoardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GameBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public GameBoardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = Math.min(width, height);
int sizeMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(sizeMeasureSpec, sizeMeasureSpec);
mPaint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int w = getWidth() / 8;
int h = getHeight() / 8;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
// choose black or white depending on the square
mPaint.setColor((row + col) % 2 == 0 ? 0xFFFFFFFF : 0xFF000000);
canvas.drawRect(w * col, h * row, w * (col + 1), h * (row + 1), mPaint);
}
}
}
}
Here I'm just drawing the squares right in the view. Now, if I were making a chess game, I would also create a Drawable subclass that would take the game model and render it. Having a separate Drawable for rendering the game makes it easy to scale to the correct size. For example, your Drawable could render at a fixed constant size, then be scaled by the View subclass to fit. The View subclass would function mostly as a controller, interpreting touch events and updating the game model.
I have a CustomViewPager inside an ObservableScrollView which looks like this:
It seems to measure the fragment but does not measure the height of the fragment which is off the screen. So I can't actually scroll up.
This is the code for the CustomViewPager:
public class CustomViewPager extends ViewPager {
private View view;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
View tab = getChildAt(getCurrentItem());
int width = getMeasuredWidth();
int tabHeight = tab.getMeasuredHeight();
if (wrapHeight) {
// Keep the current measured width.
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
int fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());
heightMeasureSpec = MeasureSpec.makeMeasureSpec(tabHeight + fragmentHeight + (int)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()), MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public int measureFragment(View view) {
if (view == null)
return 0;
view.measure(0, 0);
return view.getMeasuredHeight();
}
}
NOTE: If I add say + 1000 to heightMeasureSpec in super.onMeasure(widthMeasureSpec, heightMeasureSpec); then I can scroll the size of the fragment as well as any extra space. But obviously this is not a preferred solution.
Here is my XML file:
<FrameLayout 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"
android:orientation="vertical">
<LinearLayout
android:id="#+id/infoBox"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="240dp">
<!-- Code for other views -->
</LinearLayout>
<com.github.ksoichiro.android.observablescrollview.ObservableScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="266dp"
android:orientation="vertical">
<com.ui.customviewpager.CustomViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>
What seems to be the issue here? It seems like I am not the only one with this issue.
I implemented some of this design from this link here
ScrollView child should not have margin for the scrollView to display properly.
It seems that the margin is not taken into account when the scrollView measure its child.
Try removing margin of LinearLayout (you can use padding to reproduce the effect).
Why dont you use ObservableScrollView inside the fragment and put the viewpager inside a TouchInterceptionFrameLayout (from the same lib), then set setTouchInterceptionViewGroup of the ObservableScrollView to be the one containing your viewpager and do the logic on the TouchInterceptionListener in your activity to either move it up and down with with animation in order to mimic a smooth scrolling effect.
I hope that is that your are looking for to achieve and hopefully it will help you.
Ps: See the guy example app and source (Link), it is a bit complex but im sure you will find something.
Hello i am not much aware of ObservableScrollView but according to your problem you are not able to find out height of fragment. After searching a lot i am able to find out tutorial to get the height of fragments.
here is the link
http://adanware.blogspot.in/2012/06/android-getting-measuring-fragment.html
Hope, this will help you.
I am working on an android app which interacts with Twitter using their search API.
Everythings works well except that when I want to show the result using a ListView, only the first result is shown.
ArrayList<TwitterJSONResults> arrayList = new ArrayList<TwitterJSONResults>(data.getResults().size());
for (int i = 0; i < data.getResults().size(); i++) {
arrayList.add(data.getResults().get(i));
}
ArrayAdapter<TwitterJSONResults> resultAdapter = new ArrayAdapter<TwitterJSONResults>(
this, android.R.layout.simple_list_item_1, arrayList);
listview.setAdapter(resultAdapter);
resultAdapter.notifyDataSetChanged();
The code snippet above show how I add the results to the adapter and set this adapter to the the listview, What am I doing wrong?
Don't put ListView inside of a ScrollView :)
You can use ListView in ScrollView.
public class Utility {
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
listView.requestLayout();
}
}
Call this function right after you change ListView items, like that:
Utility.setListViewHeightBasedOnChildren(myListView);
Read source.
Thanks to #207 I realized that the problem in my case was because I was using NestedScrollView, so for me the solution was used
android:fillViewport="true"
Here my code:
<android.support.v4.widget.NestedScrollView
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
</android.support.v4.widget.NestedScrollView>
I have had this error like you when put listview inside ScrollView. And my solution is following as:
1. Create a custom listview which is non scrollable
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
2. Use above custom class for xml file
<xx.xx.NonScrollListView
android:id="#+id/lv_nonscroll_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</xx.xx.NonScrollListView>
It worked well on all OS-version for me.
Hope best for you.
It turns out I had everything in my java code working perfectly. the issue was that was using the listview inside a scrollview which is generally a bad idea. It led to listview ignoring
android:layout="wrap_content"
therefore the content was there, it just was not showing. After I remove the scrollview from my XML file, everything worked file.
Big thanks to 207 for the support :)
You can use a ListView inside a NestedScrollView.
The only thing you have to remember is to add android:nestedScrollingEnabled="true" in the xml layout in order to enable the scroll of NestedScrollView's children.
Put it in ListView xml layout
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
this is my answere
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:id="#+id/listViewRefeCalendarDay" />
</android.support.v4.widget.NestedScrollView>
View's have a minHeight but somehow are lacking a maxHeight:
What I'm trying to achieve is having some items (views) filling up a ScrollView. When there are 1..3 items I want to display them directly. Meaning the ScrollView has the height of either 1, 2 or 3 items.
When there are 4 or more items I want the ScrollView to stop expanding (thus a maxHeight) and start providing scrolling.
However, there is unfortunately no way to set a maxHeight. So I probably have to set my ScrollView height programmatically to either WRAP_CONTENT when there are 1..3 items and set the height to 3*sizeOf(View) when there are 4 or more items.
Can anyone explain why there is no maxHeight provided, when there is already a minHeight?
(BTW: some views, like ImageView have a maxHeight implemented.)
None of these solutions worked for what I needed which was a ScrollView set to wrap_content but having a maxHeight so it would stop expanding after a certain point and start scrolling. I just simply overrode the onMeasure method in ScrollView.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
This might not work in all situations, but it certainly gives me the results needed for my layout. And it also addresses the comment by madhu.
If some layout present below the scrollview then this trick wont work – madhu Mar 5 at 4:36
In order to create a ScrollView or ListView with a maxHeight you just need to create a Transparent LinearLayout around it with a height of what you want the maxHeight to be. You then set the ScrollView's Height to wrap_content. This creates a ScrollView that appears to grow until its height is equal to the parent LinearLayout.
This worked for me to make it customizable in xml:
MaxHeightScrollView.java:
public class MaxHeightScrollView extends ScrollView {
private int maxHeight;
private final int defaultHeight = 200;
public MaxHeightScrollView(Context context) {
super(context);
}
public MaxHeightScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!isInEditMode()) {
init(context, attrs);
}
}
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode()) {
init(context, attrs);
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
if (!isInEditMode()) {
init(context, attrs);
}
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
//200 is a defualt value
maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_maxHeight, defaultHeight);
styledAttrs.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
attr.xml
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeight" format="dimension" />
</declare-styleable>
example layout
<blah.blah.MaxHeightScrollView android:layout_weight="1"
app:maxHeight="90dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="#+id/commentField"
android:hint="Say Something"
android:background="#FFFFFF"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:gravity="center_vertical"
android:maxLines="500"
android:minHeight="36dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</blah.blah.MaxHeightScrollView>
(I know this does not directly answer the question but might be helpful to others looking for maxHeight functionality)
ConstraintLayout offers maximum height for its children via
app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true"
or
app:layout_constraintWidth_max="300dp"
app:layout_constrainedWidth="true"
Sample usage here.
I would have commented on whizzle's answer if I could, but thought it useful to note that in order for me to solve this problem in the context of multi-window mode in Android N, I needed to change the code slightly to this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(MeasureSpec.getSize(heightMeasureSpec) > maxHeight) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
This allows for the layout to resize to be smaller than the max height, but also prevent it from being larger than the max height. I used this is a layout class that Overrides RelativeLayout and this allowed me to create a custom dialog with a ScrollView as the child of MaxHeightRelativeLayout that does not expand the full height of the screen and also shrinks to fit within the smallest widow size in multi-window for Android N.
As mentioned above, ConstraintLayout offers maximum height for its children via:
app:layout_constraintHeight_max="300dp"
app:layout_constrainedHeight="true"
Besides, if maximum height for one ConstraintLayout's child is uncertain until App running, there still has a way to make this child automatically adapt a mutable height no matter where it was placed in the vertical chain.
For example, we need to show a bottom dialog with a mutable header TextView, a mutable ScrollView and a mutable footer TextView. The dialog's max height is 320dp,when total height not reach 320dp ScrollView act as wrap_content, when total height exceed ScrollView act as "maxHeight=320dp - header height - footer height".
We can achieve this just through xml layout file:
<?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="320dp">
<TextView
android:id="#+id/tv_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black_10"
android:gravity="center"
android:padding="10dp"
app:layout_constraintBottom_toTopOf="#id/scroll_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"
app:layout_constraintVertical_chainStyle="packed"
tools:text="header" />
<ScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black_30"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#id/tv_footer"
app:layout_constraintHeight_max="300dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tv_header">
<LinearLayout
android:id="#+id/ll_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/tv_sub1"
android:layout_width="match_parent"
android:layout_height="160dp"
android:gravity="center"
android:textColor="#color/orange_light"
tools:text="sub1" />
<TextView
android:id="#+id/tv_sub2"
android:layout_width="match_parent"
android:layout_height="160dp"
android:gravity="center"
android:textColor="#color/orange_light"
tools:text="sub2" />
</LinearLayout>
</ScrollView>
<TextView
android:id="#+id/tv_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black_50"
android:gravity="center"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/scroll_view"
tools:text="footer" />
</android.support.constraint.ConstraintLayout>
Most import code is short:
app:layout_constraintVertical_bias="1"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constrainedHeight="true"
Horizontal maxWidth usage is quite the same.
There is no way to set maxHeight. But you can set the Height.
To do that you will need to discovery the height of each item of you scrollView. After that just set your scrollView height to numberOfItens * heightOfItem.
To discovery the height of an item do that:
View item = adapter.getView(0, null, scrollView);
item.measure(0, 0);
int heightOfItem = item.getMeasuredHeight();
To set the height do that:
// if the scrollView already has a layoutParams:
scrollView.getLayoutParams().height = heightOfItem * numberOfItens;
// or
// if the layoutParams is null, then create a new one.
scrollView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, heightOfItem * numberOfItens));
Wrap your ScrollView around your a plainLinearLayout with layout_height="max_height", this will do a perfect job. In fact, I have this code in production from last 5 years with zero issues.
<LinearLayout
android:id="#+id/subsParent"
android:layout_width="match_parent"
android:layout_height="150dp"
android:gravity="bottom|center_horizontal"
android:orientation="vertical">
<ScrollView
android:id="#+id/subsScroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginEnd="15dp"
android:layout_marginStart="15dp">
<TextView
android:id="#+id/subsTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/longText"
android:visibility="visible" />
</ScrollView>
</LinearLayout>
My MaxHeightScrollView custom view
public class MaxHeightScrollView extends ScrollView {
private int maxHeight;
public MaxHeightScrollView(Context context) {
this(context, null);
}
public MaxHeightScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray styledAttrs =
context.obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView);
try {
maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.MaxHeightScrollView_mhs_maxHeight, 0);
} finally {
styledAttrs.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (maxHeight > 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
style.xml
<declare-styleable name="MaxHeightScrollView">
<attr name="mhs_maxHeight" format="dimension" />
</declare-styleable>
Using
<....MaxHeightScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mhs_maxHeight="100dp"
>
...
</....MaxHeightScrollView>
I have an answer here:
https://stackoverflow.com/a/29178364/1148784
Just create a new class extending ScrollView and override it's onMeasure method.
#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);
}
In case anyone needs it:
app:layout_constraintHeight_max="300dp"
It forces the View (that is inside a ConstraintLayout) to be 300dp as a max height. For those who want to do this programmatically, it goes like this:
val totalScreenHeight = displayMetrics.heightPixels
val layoutParams: ConstraintLayout.LayoutParams = viewThatIsInsideAConstraintLayout.layoutParams as ConstraintLayout.LayoutParams
layoutParams.matchConstraintMaxHeight = totalScreenHeight/2
viewThatIsInsideAConstraintLayout.layoutParams = layoutParams
Have you tried using the layout_weight value? If you set one it to a value greater than 0, it will stretch that view into the remaining space available.
If you had multiple views that needed to be stretched, then the value will become a weight between them.
So if you had two views both set to a layout_weight value of 1, then they would both stretch to fill in the space but they would both stretch to an equal amount of space. If you set one of them to the value of 2, then it would stretch twice as much as the other view.
Some more info here listed under Linear Layout.
i think u can set the heiht at runtime for 1 item just scrollView.setHeight(200px), for 2 items scrollView.setheight(400px) for 3 or more scrollView.setHeight(600px)
As we know devices running android can have different screen sizes. As we further know views should adjust dynamically and become the space which is appropriate.
If you set a max height you maybe force the view not to get enough space or take to less space. I know that sometimes it seems to be practically to set a max height. But if the resolution will ever change dramatically, and it will!, then the view, which has a max height, will look not appropriate.
i think there is no proper way to exactly do the layout you want. i would recommend you to think over your layout using layout managers and relative mechanisms. i don't know what you're trying to achieve but it sounds a little strange for me that a list should only show three items and then the user has to scroll.
btw. minHeight is not guaranteed (and maybe shouldn't exist either). it can have some benefit to force items to be visible while other relative items get smaller.
If anyone is considering using exact value for LayoutParams e.g.
setLayoutParams(new LayoutParams(Y, X );
Do remember to take into account the density of the device display otherwise you might get very odd behaviour on different devices. E.g:
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics d = new DisplayMetrics();
display.getMetrics(d);
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, (int)(50*d.density) ));
First get the item height in pixels
View rowItem = adapter.getView(0, null, scrollView);
rowItem.measure(0, 0);
int heightOfItem = rowItem.getMeasuredHeight();
then simply
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
scrollView.getLayoutParams().height = (int)((heightOfItem * 3)*displayMetrics .density);
if you guys want to make a non-overflow scrollview or listview, just but it on a RelativeLayout with a topview and bottomview on top and bottom for it:
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/topview"
android:layout_below="#+id/bottomview" >
I used a custom ScrollView made in Kotlin which uses maxHeight. Example of use:
<com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="100dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.antena3.atresplayer.tv.ui.widget.ScrollViewWithMaxHeight>
Here is the code of ScrollViewWidthMaxHeight:
import android.content.Context
import android.util.AttributeSet
import android.widget.ScrollView
import timber.log.Timber
class ScrollViewWithMaxHeight #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ScrollView(context, attrs, defStyleAttr) {
companion object {
var WITHOUT_MAX_HEIGHT_VALUE = -1
}
private var maxHeight = WITHOUT_MAX_HEIGHT_VALUE
init {
val a = context.obtainStyledAttributes(
attrs, R.styleable.ScrollViewWithMaxHeight,
defStyleAttr, 0
)
try {
maxHeight = a.getDimension(
R.styleable.ScrollViewWithMaxHeight_android_maxHeight,
WITHOUT_MAX_HEIGHT_VALUE.toFloat()
).toInt()
} finally {
a.recycle()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasure = heightMeasureSpec
try {
var heightSize = MeasureSpec.getSize(heightMeasureSpec)
if (maxHeight != WITHOUT_MAX_HEIGHT_VALUE) {
heightSize = maxHeight
heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST)
} else {
heightMeasure = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.UNSPECIFIED)
}
layoutParams.height = heightSize
} catch (e: Exception) {
Timber.e(e, "Error forcing height")
} finally {
super.onMeasure(widthMeasureSpec, heightMeasure)
}
}
fun setMaxHeight(maxHeight: Int) {
this.maxHeight = maxHeight
}
}
which needs also this declaration in values/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollViewWithMaxHeight">
<attr name="android:maxHeight" />
</declare-styleable>
</resources>