I've read many other SO answers but nothing seems to be what I want. What I want is a ViewPager inside a ScrollView with the heights of each page being appropriate for the content. Some of the more accepted answers on SO seem to have to take the max height of the children in the ViewPager but that leads for empty space.
WrapContentViewPager
public class WrapContentViewPager extends ViewPager {
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
View view = null;
for(int i = 0; i < getChildCount(); i++) {
view = getChildAt(i);
view.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = view.getMeasuredHeight();
if(h > height) height = h;
}
if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, view));
}
/**
* Determines the height of this view
*
* #param measureSpec A measureSpec packed into an int
* #param view the base view with already measured height
*
* #return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec, View view) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
// set the height from the base view if available
if (view != null) {
result = view.getMeasuredHeight();
}
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
The reason why this doesn't work is because if I go to the third page, which has a large height with 32 items in the list, then go back to the second page with only 3 items, there is a lot of empty space in the second page since it took the height of the third page as max.
I've tried WCViewPager library on GitHub at https://github.com/rnevet/WCViewPager and it doesn't work for me as well.
Could someone guide me to a correct solution? I am using a ViewPager with just PagerAdapter, not FragmentPagerAdapter by the way.
UPDATE
I figured out a better way to solve it.
`public class WrapContentViewPager extends ViewPager {
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
// Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
// At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
// super has to be called in the beginning so the child views can be initialized.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int position = getCurrentItem();
View child = this.findViewWithTag("view"+position);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int height = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
// super has to be called again so the new specs are treated as exact measurements
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}`
and in my PagerAdapter I changed the method instantiateItem
#Override
public #NonNull Object instantiateItem(#NonNull ViewGroup view, int position) {
View layout = inflater.inflate(R.layout.layout, view, false);
layout.setTag("view" + position);
}
This solution remeasures the height at the current page every time it is moved. getChildAt(getCurrentItem()) gives different results so it's not reliable.
Related
I tried to refer[https://medium.com/#nikhil4092/how-to-have-a-height-wrapping-viewpager-when-images-have-variable-heights-on-android-60b18e55e72e] this link for making the viewpager's height wrap_content but it didn't work.I've tried several questions of stackoverflow but none of them could address my problem.When I'm giving the height as wrap_content nothing is shown
Code:
public class HeightWrappingViewPager extends ViewPager {
public HeightWrappingViewPager(Context context) {
super(context);
}
public HeightWrappingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
// Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
// At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
// super has to be called in the beginning so the child views can be initialized.
super.onMeasure(widthMeasureSpec, 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 has to be called again so the new specs are treated as exact measurements
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
XML:
<com.project.test.HeightWrappingViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
I'm not sure is it helps you.
I used the code below to images with different height.
Almost like your code, but i save height like a field.
public class MeasuredViewPager extends ViewPager {
private int mMaxHeight = 0;
public MeasuredViewPager(Context context) {
super(context);
}
public MeasuredViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
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 > mMaxHeight) mMaxHeight = h;
}
if (mMaxHeight != 0) heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
I am creating a custom ViewGroup. I does need to have 2 FrameLayout one above the other; the one that stays to the bottom must be 20dp, while the other one must cover the rest of the view.
onMeasure
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
final View content = getChildAt(CONTENT_INDEX);
final View bar = getChildAt(BAR_INDEX);
content.measure(
MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(heightSize - getPixels(BAR_HEIGHT), MeasureSpec.EXACTLY)
);
bar.measure(
MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getPixels(BAR_HEIGHT), MeasureSpec.EXACTLY)
);
onLayout
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mInLayout = true;
final View content = getChildAt(CONTENT_INDEX);
final View bar = getChildAt(BAR_INDEX);
if (content.getVisibility() != GONE) {
content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
}
if (bar.getVisibility() != GONE) {
bar.layout(0, content.getMeasuredHeight(), bar.getMeasuredWidth(), 0);
}
mInLayout = false;
mFirstLayout = false;
}
}
The views I am adding to this custom ViewGroup
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mContentContainer = new FrameLayout(getContext());
mContentContainer.setLayoutParams(lp);
mBarContainer = new FrameLayout(getContext());
mBarContainer.setLayoutParams(lp);
// ... adding stuff to both containers ....
addView(mContentContainer, 0);
addView(mBarContainer, 1);
The issue
mContentContainer gets rendered correctly (from top=0 to bottom=(totalHeight - bar height) and match parent for the width), while the bar is not rendered.
The last parameter in the View#layout() method is the bottom of the View. For your bar, you're passing 0, but it should be the height of your custom View, which you can figure from the t and b values passed into onLayout().
bar.layout(0, content.getMeasuredHeight(), bar.getMeasuredWidth(), b - t);
I wrote the following ViewGroup
public class myViewGroup extends ViewGroup{
List<View> qResult;
List<Point> qLoc;
ImageView qImage;
public QueryViewLayout(Context context){
super(context);
}
public QueryViewLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QueryViewLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
qResult = new LinkedList<View>();
qLoc = new LinkedList<Point>();
qImage = null;
}
public void addMainView(ImageBorderView view){
qImage = view;
super.removeAllViews();
super.addView(view);
}
public void addResultView(View result, Point loc){
super.addView(result);
qResult.add(result);
qLoc.add(loc);
}
/**
* Any layout manager that doesn't scroll will want this.
*/
#Override
public boolean shouldDelayChildPressedState() {
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
// Measurement will ultimately be computing these values.
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
// Only main view affects the layouts measure
if (qImage != null) {
if (qImage.getVisibility() != GONE) {
// Measure the child.
qImage.measure(widthMeasureSpec, heightMeasureSpec);
maxWidth = qImage.getMeasuredWidth();
maxHeight = qImage.getMeasuredHeight();
childState = qImage.getMeasuredState();
}
}
for (View child:qResult){
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
child.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
}
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Report our final dimensions.
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
int parentLeft = left + getPaddingLeft();
int parentRight = right - getPaddingRight();
final int parentTop = top + getPaddingTop();
final int parentBottom = bottom - getPaddingBottom();
if (qImage == null) return;
qImage.layout(parentLeft, parentTop, parentRight, parentBottom);
Iterator<Point> loc = qLoc.iterator();
for (View child:qResult) {
Point p = loc.next();
if (child.getVisibility() != GONE) {
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
Point locOnView = qImage.projectOnView(p);
width = (width < (int) Math.max(parentRight - (int) locOnView.x, locOnView.x - parentLeft)) ?
width : (parentLeft + parentRight)/2;
height = (height < (int) Math.max(parentBottom - (int) locOnView.y, locOnView.y - parentTop)) ?
height : (parentBottom + parentTop)/2;
int x = (width < (parentRight - (int) locOnView.x)) ? (int) locOnView.x : (parentRight - width);
int y = (height < parentBottom - (int) locOnView.y) ? (int) locOnView.y : (parentBottom - height);
// Place the child.
child.layout(x, y, x + width, y + height);
}
}
}
}
It is supposed to show some arbitrary view on top of an image, given a location for that view, when I use a GridView as the arbitrary view, even though I have defined a certain width for the GridView it is forced to have a width as large as the frame. In the measure phase I changed the mode to
MeasureSpec.AT_MOST
for both width and height of the overlay view, but this does not seem to work, can someone please help.
here is the xml where I, inflate the GridView from
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/result_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:columnWidth="#dimen/result_view_column_width"
android:numColumns="2"
android:verticalSpacing="2dp"
android:horizontalSpacing="2dp"
android:stretchMode="none"
android:gravity="center"
android:layout_margin = "2dp"
android:background="#drawable/solid_with_shadow" />
After a lot of trial and error, replacing
child.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
with
measureChild(child, MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
worked for me, I am not sure why, but a wild guess would be calling measure on a child does not read all the xml props, but measureChild(child, ...) does.
How get a 'ScrollView' from ListView ?
I want implement that function
public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
final int headerHeight = getActivity().findViewById(R.id.fragment_mensagem_list_header)
.getHeight() - getActivity().getActionBar().getHeight();
final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight;
final int newAlpha = (int) (ratio * 255);
mActionBarBackgroundDrawable.setAlpha(newAlpha);
}
((NotifyingScrollView) LISTVIEWHERE ).setOnScrollChangedListener(mOnScrollChangedListener);
You can add a OnScrollListener for a listview and get the callbacks of scroll states like below.
yourListViewReference.setOnScrollListener(new OnScrollListener(){
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
final int headerHeight = getActivity().findViewById(R.id.fragment_mensagem_list_header)
.getHeight() - getActivity().getActionBar().getHeight();
final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight;
final int newAlpha = (int) (ratio * 255);
mActionBarBackgroundDrawable.setAlpha(newAlpha);
}
});
}
Please Be more Clear with the Question on what you are trying to achieve !
Why are you adding ScrollView to a Listview ?
Adding ScrollView to a listview is a bad practice in android
because listView already is provided with the scroll functionality
If you add a scrollView to a listView android will get confused
on which Touch to detect, ListView or ScrollView
If you are adding ScrollView to ListView Please re-Think on your
design beccause it is not a good practice
Quoting from the docs
You should never use a ScrollView with a ListView, because ListView takes care of its own vertical scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView to display its entire list of items to fill up the infinite container supplied by ScrollView.
You can add your views as header and footer to your listview. The header and footer will scroll.
You can also Hack your way to solve this, Check This Solution from tacone, But i would not recommend this since its a bad practice, and may lead to performance issues
ExpandableHeightGridView.java:
package com.example;
public class ExpandableHeightGridView extends GridView
{
boolean expanded = false;
public ExpandableHeightGridView(Context context)
{
super(context);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs,
int defStyle)
{
super(context, attrs, defStyle);
}
public boolean isExpanded()
{
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// HACK! TAKE THAT ANDROID!
if (isExpanded())
{
// Calculate entire height by providing a very large height hint.
// View.MEASURED_SIZE_MASK represents the largest height possible.
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
else
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded)
{
this.expanded = expanded;
}
}
Include it in your layout like this:
<com.example.ExpandableHeightGridView
android:id="#+id/myId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:horizontalSpacing="2dp"
android:isScrollContainer="false"
android:numColumns="4"
android:stretchMode="columnWidth"
android:verticalSpacing="20dp" />
Lastly you just need to ask it to expand:
mAppsGrid = (ExpandableHeightGridView) findViewById(R.id.myId);
mAppsGrid.setExpanded(true);
Let me know if you need any help!
Use the OnScrollListener for ListView and GridView, you can get the 't' by the below formula
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View c = view.getChildAt(0);
if (c != null) {
int t = -c.getTop() + view.getFirstVisiblePosition() * c.getHeight();
final int headerHeight = mHeaderImage.getHeight() - getActionBar().getHeight();
final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight;
final int newAlpha = (int) (ratio * 255);
((MainActivity) mActivity).getDrawableBGActionBar().setAlpha(newAlpha);
}
}
I'm having a little difficulties while trying to get a certain layout to work: I want to have list. List does not have to be scrollable, but should be shown completely. But the page itself should be able to scroll (with the lists in it), if the total content ist higher than the screen.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linear_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#ff181818"
>
<Textview android:id="#+id/my_text" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
<Textview android:id="#+id/headertext" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
<ListView
android:id="#+id/my_list1"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
/>
</LinearLayout>
</ScrollView>
it only uses a small part of the screen (about 2 lines per list), instead of filling the available height, and the lists themselves can be scrolled. How can I change the layout to always show the whole lists but have the screen be scrollalbe?
The solution I used is to replace ListView with LinearLayout. You can create all your items inside LinearLayout, they will all be displayed. So there's really no need to use ListView.
LinearLayout list = (LinearLayout)findViewById(R.id.list_recycled_parts);
for (int i=0; i<products.size(); i++) {
Product product = products.get(i);
View vi = inflater.inflate(R.layout.product_item, null);
list.addView(vi);
}
As #Alex noted in the accepted answer that LinearLayout is hardly a replacement. I had a problem where LinearLayout was not an option, that's when i came across this blog. I will put the code here for reference purposes. Hope it helps someone out there!
public class UIUtils {
/**
* Sets ListView height dynamically based on the height of the items.
*
* #param listView to be resized
* #return true if the listView is successfully resized, false otherwise
*/
public static boolean setListViewHeightBasedOnItems(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter != null) {
int numberOfItems = listAdapter.getCount();
// Get total height of all items.
int totalItemsHeight = 0;
for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
View item = listAdapter.getView(itemPos, null, listView);
item.measure(0, 0);
totalItemsHeight += item.getMeasuredHeight();
}
// Get total height of all item dividers.
int totalDividersHeight = listView.getDividerHeight() *
(numberOfItems - 1);
// Set list height.
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
listView.setLayoutParams(params);
listView.requestLayout();
return true;
} else {
return false;
}
}
}
Usage:
//initializing the adapter
listView.setAdapter(adapter);
UIUtils.setListViewHeightBasedOnItems(listView);
//whenever the data changes
adapter.notifyDataSetChanged();
UIUtils.setListViewHeightBasedOnItems(listView);
You can make your own customlistview. (It can extends ListView/ExpandableListView/GridView) and override the onMeasure method with this. With this you'll never need to call a function or anything. Just use it in your xml.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
I had a ListView in my layout and wanted to use a library which can't handle a ListView here because it wraps it into a ScrollView. The best solution for me is based on FedorĀ“s answer.
Since I already got an ArrayAdapter for the ListView I wanted to re-use it:
LinearLayout listViewReplacement = (LinearLayout) findViewById(R.id.listViewReplacement);
NamesRowItemAdapter adapter = new NamesRowItemAdapter(this, namesInList);
for (int i = 0; i < adapter.getCount(); i++) {
View view = adapter.getView(i, null, listViewReplacement);
listViewReplacement.addView(view);
}
For me this works fine because I just need to display dynamic data varying from 1 to 5 elements. I just had to add my own divider.
If someone still has the problem then you can make customList and add onMesure() method just like I implemented it:
public class ScrolleDisabledListView extends ListView {
private int mPosition;
public ScrolleDisabledListView(Context context) {
super(context);
}
public ScrolleDisabledListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrolleDisabledListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Record the position the list the touch landed on
mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
return super.dispatchTouchEvent(ev);
}
if (actionMasked == MotionEvent.ACTION_MOVE) {
// Ignore move events
return true;
}
if (actionMasked == MotionEvent.ACTION_UP) {
// Check if we are still within the same view
if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
super.dispatchTouchEvent(ev);
} else {
// Clear pressed state, cancel the action
setPressed(false);
invalidate();
return true;
}
}
return super.dispatchTouchEvent(ev);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
Check this out:
ListView ignoring wrap_content
Using android:layout_height and android:layout_weight solved it for me:
<ListView
android:layout_height="0dp"
android:layout_weight="1"
/>
I just did it using setting params of ListView
public static void setListViewHeightBasedOnChildren(ListView listView) {
//this comes from value from xml tag of each item
final int HEIGHT_LARGE=75;
final int HEIGHT_LARGE=50;
final int HEIGHT_LARGE=35;
ViewGroup.LayoutParams params = listView.getLayoutParams();
int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
switch(screenSize) {
case Configuration.SCREENLAYOUT_SIZE_LARGE:
params.height =(int) (HEIGHT_LARGE*size);
break;
case Configuration.SCREENLAYOUT_SIZE_NORMAL:
params.height =(int) (HEIGHT_NORMAL*size);
break;
case Configuration.SCREENLAYOUT_SIZE_SMALL:
params.height =(int) (HEIGHT_SMALL*size);
break;
}
listView.setLayoutParams(params);
}
I don't have a static header, but using HussoM's post as a clue, here is what I was able to get to work. In my scenario, the height of the items in the list was non-uniform, due to variable text sentences in each of the items, and I am using wrap_content for the height and match_parent for the width.
public class NonScrollableListView extends ListView {
public NonScrollableListView(Context context) {
super(context);
}
public NonScrollableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* Measure the height of all the items in the list and set that to be the height of this
* view, so it appears as full size and doesn't need to scroll.
* #param widthMeasureSpec
* #param heightMeasureSpec
*/
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
ListAdapter adapter = this.getAdapter();
if (adapter == null) {
// we don't have an adapter yet, so probably initializing.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int totalHeight = 0;
// compute the height of all the items
int itemCount = adapter.getCount();
for (int index=0; index<itemCount; index++) {
View item = adapter.getView(index, null, this);
// set the width so it can figure out the height
item.measure(widthMeasureSpec, 0);
totalHeight += item.getMeasuredHeight();
}
// add any dividers to the height
if (this.getDividerHeight() > 0) {
totalHeight += this.getDividerHeight() * Math.max(0, itemCount - 1);
}
// make it so
this.setMeasuredDimension(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY));
}
}
If all items has the same height
int totalItemsHeight = baseDictionaries.size() * item.getMeasuredHeight();
int totalDividersHeight = listView.getDividerHeight() * (baseDictionaries.size() - 1);
int totalPadding = listView.getPaddingBottom() + listView.getPaddingTop();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) listTranslationWords.getLayoutParams();
lp.height = totalItemsHeight + totalDividersHeight + totalPadding;
listTranslationWords.setLayoutParams(lp);
Iam supprised no one see this.U cant have two scrolls on the same layout. 1st u have a scrollview and then u have a list, i bet u are killing some android good practices there.
If you want a simple solution to this problem without extending ListView class, this is a solution for you.
mListView.post(new Runnable() {
#Override
public void run() {
int height = 0;
for(int i = 0; i < mListView.getChildCount();i++)
height += mListView.getChildAt(i).getHeight();
ViewGroup.LayoutParams lParams = mListView.getLayoutParams();
lParams.height = height;
mListView.setLayoutParams(lParams);
}
});
In my case, I had ListView inside ScrollView and scrollview was shrinking listview by default. So I just add this in my ScrollView and it worked for me
android:fillViewport="true"
Set android:layout_height="fill_parent" in your LinearLayout