Measure height of listview items - java

I want to measure the height of each item in a ListView before the rendering.
I added this method to my custom ListView
public void measureItemsHeight() {
mItemCount = getAdapter().getCount();
if(mItemOffsetY == null){
mItemOffsetY = new int[mItemCount];
}
for(int i = 0; i < mItemCount; ++i){
View view = getAdapter().getView(i, null, this);
view.measure(0, 0);
mItemHeight[i] = view.getMeasuredHeight();
}
}
Then, inside my activity's onCreate():
mListView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mListView.measureItemsHeight();
}
}
);
The values i get are different than the ones reported by getHeight(), eg getMeasuredHeight() returns 48, and the getHeight() returns 72.
The layout i use for the items is the simple_list_item
It looks to me this like a hdpi and xhdpi problem, is there a way to fix this? The device I use to debug is the Samsung SGS2, maybe that is the problem.

I would suggest you to use custom list,Make separate layout for list row.Assign following property to that views layout:android:layout_height=wrap_content. Thats it.Good Luck!

Related

Android numberpicker edge scrolling glitch

I've created a number picker with some string values.
public void setValues() {
boolean defaultAvaliable = false;
int defaultRow = 0;
String[] values = new String[pickValues.size()];
for (int i = 0; i < pickValues.size(); i++) {
if (pickValues.get(i).equals("01:00")) {
defaultAvaliable = true;
defaultRow = i;
}
values[i] = pickValues.get(i);
}
timePicker.setMaxValue(values.length - 1);
timePicker.setMinValue(0);
timePicker.setWrapSelectorWheel(false);
timePicker.setDisplayedValues(values);
if (defaultAvaliable) {
timePicker.setValue(defaultRow);
}
}
And when I scroll to edge values (first or last) the picker first jumps to that item in a weird, not smooth way and after that when i try to go back to other values it doesn't scroll. The picker glitches on the edge value and after some time it finally goes back. In some way it looks like I would scroll over the edge value and it needs some scrolling to get back.
Has anyone else had this problem or does anyone have any suggestions what to check?
EDIT 1:
So I've got to he point that I know the problem is when I change the font size in my numberpicker, and don't know why. As soon as I set the font size over 39 it starts to get stuck on first and last item.
private void updateView(View view) {
if(view instanceof EditText){
((EditText) view).setTextSize(39);
((EditText) view).setTextColor(getResources().getColor(R.color.mp_white));
((EditText) view).setFocusable(false);
}
}
Does anyone know what is the row size?
Or if I have to change the size of it, so it detects the scrolling correctly?

RecyclerView recalculate WRAP_CONTENT

With version 23.2 we can now use WRAP_CONTENT for recyclerView height, which is great. I am doing this, however I want to recalculate the height after an item is added (or removed) to the adapter (thus increasing or decreasing the height).
My particular RecyclerView is starting with 1 item, and then adding items as the user makes selection. So I need the RecyclerView layout to increase in height, up to a point. Ideally this would happen with a smooth animation when the list increases or decreases.
How can we make it WRAP_CONTENT after it has been laid out?
Tried:
recyclerview.requestLayout();
recyclerview.invalidate();
I would expect it to work with View.invalidate().
If that does not work, try to call either requestLayout or invalidate on the parent view.
How RecyclerView Resize itself based on with the new LayoutManger
The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.
Due to this change, make sure to double check the layout parameters of your item views: previously ignored layout parameters (such as MATCH_PARENT in the scroll direction) will now be fully respected.
If you have a custom LayoutManager that does not extend one of the built in LayoutManagers, this is an opt-in API - you’ll be required to call setAutoMeasureEnabled(true) as well as make some minor changes as detailed in the Javadoc of the method.
Note that although RecyclerView animates its children, it does not animate its own bounds changes. If you would like to animate the RecyclerView bounds as they change, you can use the Transition APIs.
Please read this
Option I
Did you see this answer?
It's not using the recyclerView's WRAP_CONTENT, but it might work.
You can also create your own custom recyclerView (extends RecyclerView) and override the onMeasure() method there instead using the layoutManager in the link.
Option II
Try to set the layout params before drawing the view. I haven't checked if it is called when recyclerView layout changes, but if it does, then it will work. Something like this (in your Activity/Fragment onCreate()/onCreateView() method:
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
YourParentLayoutType.LayoutParams params = (YourParentLayoutType.LayoutParams) recyclerView.getLayoutParams();
params.height = YourParentLayoutType.LayoutParams.WRAP_CONTENT;
recyclerView.setLayoutParams(params);
return true;
}
});
Use your recyclerView's parent layout type instead of YourParentLayoutType in the code.
I'm not sure this will work when layout refreshes, but maybe worth a try.
Use this class:
Please use 23.2.1 as 23.2 was way buggy.
Also have you tried to call notifyDataSetChanged on the recyclerview adapter , as far as i think it should expand without problem if you have given wrap_content as height of recyclerview
else u can use this class:
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
public class MyLinearLayoutManager extends LinearLayoutManager {
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
if (getOrientation() == HORIZONTAL) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
heightSpec,
mMeasuredDimension);
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
measureScrapChild(recycler, i,
widthSpec,
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
int widthDesired = Math.min(widthSize,width);
setMeasuredDimension(widthDesired, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
// For adding Item Decor Insets to view
super.measureChildWithMargins(view, 0, 0);
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(
widthSpec,
getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view),
p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(
heightSpec,
getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view) ,
p.height);
view.measure(childWidthSpec, childHeightSpec);
// Get decorated measurements
measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
recycler.recycleView(view);
}
}

getLayoutParams().height returns -2 even after onGlobalLayout()

I have an android dialog which i want to position in a specific position in its window.
I'm using API 8
how come int a == -2 and int b == 153 are not positive?
what is the difference between
getLayoutParams().height;
mToolTipLayout.getHeight();
I have the following code
public void initViews(int orientation) {
mToolTipLayout = ((LinearLayout) findViewById(R.id.tooltip_layout));
ViewTreeObserver vto = mToolTipLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mToolTipLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
setPosition();
}
});
private void setPosition() {
int a = mToolTipLayout.getLayoutParams().height;
int b = mToolTipLayout.getHeight();
}
Layout params specify how the measure and layout process should work. They are not updated in the measure/layout process. -2 is the value for WRAP_CONTENT.
The measures themselves are available in the views themselves, not their layout params. 153 is the measured pixel height in your case, measured with WRAP_CONTENT spec.
Yes, -2 means WRAP_CONTENT. To get a height of a layout in this case you cannot use getLayoutParams().height or getHeight() (-2 and 0 respectively). You should get a height of inner view (for instance, with view.getLayoutParams().height). Probably add paddings.

Trouble laying out ImageViews programmatically in RelativeLayout

I'm trying to add five or so ImageViews to a RelativeLayout, with each new one being to the right of the previous one. However, what I get is (apparently) all of them stacked up on each other, as if they were all ALIGN_PARENT_LEFT. Relevant code snippet:
ImageView[] foo = new ImageView[levels];
for (int i = 0; i < levels; i++) {
foo[i] = new ImageView(context);
layoutFoo.addView(foo[i]);
foo[i].setId(10 + i);
if (i == 0) {
RelativeLayout.LayoutParams fooParams = (RelativeLayout.LayoutParams) foo[i].getLayoutParams();
fooParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
foo[i].setLayoutParams(fooParams);
} else {
RelativeLayout.LayoutParams fooParams = (RelativeLayout.LayoutParams) foo[i].getLayoutParams();
fooParams.addRule(RelativeLayout.RIGHT_OF, foo[i-1].getId());
foo[i].setLayoutParams(fooParams);
}
}
Any hints what I'm doing wrong? It shouldn't matter that the ImageViews don't have any width (because I haven't assigned a bitmap to them) yet, right?
Edit: the problem turned out to be not setting a height and width for the ImageView. Solution is to set them either with setLayoutParams() or with the constructor as in JustWork's example.
You must add your ImageViews after setting properties them.
Move this code to end of the for loop:
layoutFoo.addView(foo[i]);
By the way you don't need to ImageView[]. You can accomplish it with one ImageView.
UPDATE:
Here is the code you can do same thing with:
ImageView foo;
RelativeLayout.LayoutParams fooParams;
for (int i = 0; i < levels; i++) {
foo = new ImageView(context);
foo.setId(10 + i);
fooParams = (RelativeLayout.LayoutParams) foo.getLayoutParams(); // I don't understand why you are trying to get params of which hasn't been specified before.
// If I were you, I would do like:
fooParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i == 0) {
fooParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
} else {
fooParams.addRule(RelativeLayout.RIGHT_OF, foo.getId()-1);
}
foo.setImageBitmap(yourBitmap); // Set bitmap.
foo.setLayoutParams(fooParams);
layoutFoo.addView(foo);
}
I think the easiest way to place those images will be using a gallery. Although it's deprecated, I think it's a better idea to do it that way.

Displaying empty rows in Android listview

So I'm wondering how to go about filling a ListView with empty rows. The ListView is populated via SQLite db so say for instance there is only 3 items in the list I want to fill the rest of the screen with empty rows. Here is a screenshot of what I mean. Yes I know it's from iPhone but it demonstrates what I mean:
The cheap way is to add extra "rows" in your layout xml. Any extra rows will be cut off by the screen. This can get messy: a higher-res screen might require you add many extra TextViews. Since they get cut off, I suppose you could add as many as you'd like. It would look something like this:
<LinearLayout>
<ListView></ListView>
<TextView/>
<TextView/>
<TextView/>
...
</LinearLayout>
Another option is to add extra rows to your ListView, as mentioned previously. If you are bound to an array, add extra blank rows to the array and handle it appropriately. Coincidentally, if you are using a Cursor you can use a MaxtrixCursor & MergeCursor as described in this answer
I ended up using a combination of these methods. I try my best to calculate the number of rows I want to add to my ListView, but I error on the side of caution and have a couple TextViews underneath my ListView that make it all come together.
When you create the Array the is going to be bound to the ListView you just need to add a few rows at the end of the Array with empty strings.
Using a textview below the listview seems to give the illusion of what I was going for.
#kabir's solution works. Now if you are like me (wanted to have two alternate colors in background, this is his dispached method rewritten (or let's say edited)
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
int caseLastChildBottom = -1;
int caselastChildHeight = -1;
int caseNrOfLines = -1;
//makes the colors follow the order (alternateColor1 - alternateColor2 - alternateColor1 - etc.)
int plusIndex = 1;
if (this.getChildCount() % 2 == 0)
plusIndex = 0;
// ListView's height
final int currentHeight = getMeasuredHeight();
// this will let you know the status for the ListView, fitting/not fitting content
final int scrolledHeight = computeVerticalScrollRange();
//empty listview (no item)
if (scrolledHeight == 0) {
//no childs exist so we take the top
caseLastChildBottom = 0;
//last child doesn't exist, so we set a default height (took the value in dp in the item row's height)
caselastChildHeight = convertDpToPx(DEFAULT_CHILD_ROW_S_HEIGHT);
// determine the number of lines required to fill the ListView
caseNrOfLines = currentHeight / caselastChildHeight;
}
//there is a remaining gap to fill
else {
final View lastChild = getChildAt(getChildCount() - 1);
if (lastChild == null) return;
// values used to know where to start drawing lines
caseLastChildBottom = lastChild.getBottom();
// last child's height(use this to determine an appropriate value for the row height)
caselastChildHeight = lastChild.getMeasuredHeight();
// determine the number of lines required to fill the ListView
caseNrOfLines = (currentHeight - caseLastChildBottom) / caselastChildHeight;
}
// values used to know where to start drawing lines
final int lastChildBottom = caseLastChildBottom;
// last child's height(use this to determine an appropriate value for the row height)
final int lastChildHeight = caselastChildHeight;
// determine the number of lines required to fill the ListView
final int nrOfLines = caseNrOfLines;
int i = 0;
for (i = 0; i < nrOfLines; i++) {
Rect r = new Rect(0, lastChildBottom + i * lastChildHeight, getMeasuredWidth(), lastChildBottom + (i + 1) * lastChildHeight);
canvas.drawRect(r, (i + plusIndex) % 2 == 0 ? alternateColorView1 : alternateColorView2);
}
//is there a gap at the bottom of the list
if(currentHeight - (nrOfLines *lastChildHeight) > 0){
Rect r = new Rect(0, lastChildBottom + i * lastChildHeight,getMeasuredWidth(), currentHeight);
canvas.drawRect(r, (i + plusIndex) % 2 == 0 ? alternateColorView1 : alternateColorView2);
}
return;
}
I forgot these two Paint colors (just as #kabir declared the colors):
alternateColorView1.setColor(....);
alternateColorView1.setStyle(Paint.Style.FILL);
alternateColorView2.setColor(....);
alternateColorView2.setStyle(Paint.Style.FILL);

Categories