ListView item height is always same (and wrong) - android

I'm trying to resize ListView to show all items.
final ListAdapter adapter = timeline.getAdapter();
int totalHeight = 0;
final int desiredWidth = View.MeasureSpec.makeMeasureSpec(timeline.getWidth(), View.MeasureSpec.AT_MOST);
for (int i = 0; i < adapter.getCount(); i++) {
final View listItem = adapter.getView(i, null, timeline);
listItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
Log.v(TAG, "Item size = " + listItem.getMeasuredHeight()); // Always 38
totalHeight += listItem.getMeasuredHeight();
}
final ViewGroup.LayoutParams params = timeline.getLayoutParams();
params.height = totalHeight;
timeline.setLayoutParams(params);
timeline.requestLayout();
Why is listItem.getMeasuredHeight() always 38, disregard android:layout_height specified in item's XML layout.
The getView() method is pretty standard:
#Override
public View getView(final int position, View view, final ViewGroup parent) {
final ViewHolder holder;
if (view == null) {
final LayoutInflater li = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = li.inflate(R.layout.timeline_item, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
final ActivityLogItem log = (ActivityLogItem) getItem(position);
holder.time.setText(format.print(log.getRecordedTime().withZone(DateTimeZone.getDefault())));
holder.time.setTextColor(log.isImportant() ? view.getResources().getColor(R.color.orange) : Color.WHITE);
holder.event.setText(log.getLocalizedType(view.getResources()));
holder.event.setTextColor(log.isImportant() ? view.getResources().getColor(R.color.orange) : Color.BLACK);
return view;
}

Related

Programmatically building overlay layout after changing View.GONE to View.VISIBLE still doesn't get correct position

I'm trying to build a dynamic help overlay screen for my app. I have the basics working fine for the elements that are already visible or invisible, but when elements are initially Visibility.GONE (which I'm changing to visible before building the view) they still are found with position 0,0 and size 0x0.
I've tried adding short wait() loops, onLayoutChangedListener(), and getViewTreeObserver().addOnGlobalLayoutListener() which did nothing.
in onOptionsSelected():
HelpUtils helpUtils = new HelpUtils(mView, requireContext());
final View helpView = helpUtils.getHelpView();
final View mainLayout = helpUtils.getMainLayout();
View shiftFragment = mainLayout.findViewById(R.id.current_shift_layout);
Map<View, Integer> visibilityMap = new HashMap<>();
for (int i = 0; i < ((ViewGroup) shiftFragment).getChildCount(); i++) {
View child = ((ViewGroup)shiftFragment).getChildAt(i);
visibilityMap.put(child, child.getVisibility());
child.setVisibility(View.VISIBLE);
}
final ViewGroup viewGroup = helpUtils.getHelpViewGroup(targets);
if (firstTime) {
((FrameLayout)helpView).addView(viewGroup);
}
helpView.setVisibility(View.VISIBLE);
HelpUtils class:
public class HelpUtils {
final private View mView;
final private Context mContext;
public HelpUtils(View view, Context context) {
mView = view;
mContext = context;
}
public View getMainLayout() {
return mView.getRootView().findViewById(R.id.main_layout);
}
public View getHelpView() {
return getMainLayout().findViewById(R.id.help_view);
}
private void recursiveWalkViews(ViewGroup viewGroup, SparseArray<View> result, Set<Integer> targets) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View v = viewGroup.getChildAt(i);
if(v instanceof ViewGroup) {
recursiveWalkViews(((ViewGroup) v), result, targets);
} else {
if (targets.contains(v.getId())) {
result.put(v.getId(), v);
}
}
}
}
public SparseArray<View> getViewMap(Set<Integer> targets) {
SparseArray<View> map = new SparseArray<>();
ViewGroup view = (ViewGroup) mView;
recursiveWalkViews(view, map, targets);
return map;
}
private int getActionBarHeight() {
int[] textSizeAttr = new int[]{R.attr.actionBarSize};
TypedArray a = mContext.obtainStyledAttributes(new TypedValue().data, textSizeAttr);
int height = a.getDimensionPixelSize(0, 0);
a.recycle();
return height;
}
/**
* Match the layout of a new {#link TextView} to the layout of an existing view.
* #param source The {#link View} to be matched to.
* #param txt The text for the new {#link TextView}
* #return A new {#link TextView}
*/
public TextView matchParams(View source, String txt) {
int x, y;
float sp, size;
int[] location = new int[2];
int offset;
source.getLocationInWindow(location);
x = location[0];
y = location[1];
offset = source.getPaddingLeft();
size = ((TextView) source).getTextSize();
sp = size/mContext.getResources().getDisplayMetrics().scaledDensity;
TextView textView = new TextView(mContext);
textView.setWidth(source.getWidth() - source.getPaddingRight());
textView.setTextSize(14);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if(source instanceof MaterialButton) {
params.topMargin = y + offset + ((MaterialButton)source).getPaddingBottom() - getActionBarHeight();
} else {
params.topMargin = y + offset - getActionBarHeight();
}
params.leftMargin = x + 8;
textView.setLayoutParams(params);
textView.setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
textView.setText(txt);
return textView;
}
public ViewGroup getHelpViewGroup(Map<Integer, String> helpViews) {
FrameLayout layout = new FrameLayout(mContext);
SparseArray<View> views = getViewMap(helpViews.keySet());
List<TextView> textViews = new ArrayList<>(views.size());
for (int i = 0; i < views.size(); i++) {
View view = views.valueAt(i);
textViews.add(matchParams(views.valueAt(i), helpViews.get(views.keyAt(i))));
}
for (TextView textView : textViews) {
layout.addView(textView);
}
return layout;
}
}
Layout Inspector shows the overlay views are drawn at 0,0 with a width of 0, but the views that were changed to VISIBLE are in the correct place.
In onOptionsSelected() wrap your new view creation in a Runnable() from the target view (I'm guessing that's shiftFragment since that's where you're setting all the views to View.VISIBLE.
Basically make the following changes.
before:
for (int i = 0; i < ((ViewGroup) shiftFragment).getChildCount(); i++) {
View child = ((ViewGroup)shiftFragment).getChildAt(i);
visibilityMap.put(child, child.getVisibility());
child.setVisibility(View.VISIBLE);
}
final ViewGroup viewGroup = helpUtils.getHelpViewGroup(targets);
if (firstTime) {
((FrameLayout)helpView).addView(viewGroup);
}
helpView.setVisibility(View.VISIBLE);
after:
for (int i = 0; i < ((ViewGroup) shiftFragment).getChildCount(); i++) {
View child = ((ViewGroup)shiftFragment).getChildAt(i);
visibilityMap.put(child, child.getVisibility());
child.setVisibility(View.VISIBLE);
}
shiftFragment.post(new Runnable() {
#Override
public void run() {
final ViewGroup viewGroup = helpUtils.getHelpViewGroup(targets);
if (firstTime) {
((FrameLayout)helpView).addView(viewGroup);
}
helpView.setVisibility(View.VISIBLE);
);
The snippet below does the following:
Gets the parent view and posts a Runnable on the UI thread. This
ensures that the parent lays out its children before calling the
getHitRect() method. The getHitRect() method gets the child's hit
rectangle (touchable area) in the parent's coordinates.
from Manage touch events in a ViewGroup

ListView first item not visible/updating Android

I have 3 components in my layout ListView, EditText, Button.
When the user enters text in the edittext and hits the Button, the app makes an Api call, my just added text is added to an existing list,and fetches the list of items. Now, my issue is that that the api call is successful and I get the list with my new item properly. But when I show it in the listview, the first item in the ListView is never visible. The text I add after the first text are visible properly, even if I have a list of 10 or more items, the first item never shows. Also, Iam showing the listview and other components inside a scrollview to take full length.
Here is my code:-
public class ItemListAdapter : BaseAdapter
{
public ItemListAdapter(Activities.Detail detail, List<Models.ListOfItems> itemList)
{
// TODO: Complete member initialization
this.detail = detail;
this.itemList = itemList;
inflater = detail.LayoutInflater;
}
public override int Count
{
get { return itemList.Count; }
}
public override Java.Lang.Object GetItem(int position)
{
return position;
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = inflater.Inflate(Resource.Layout.item_row_layout, null);
}
txtAuthorName = convertView.FindViewById<TextView>(Resource.Id.txtCommentAuthor);
txtItemName = convertView.FindViewById<TextView>(Resource.Id.txtTime);
txtItemDate = convertView.FindViewById<TextView>(Resource.Id.txtItemDate);
var mData = itemList.ElementAt(position);
txtAuthorName.Text = mData.AuthorDisplayName;
txtItemName.Text = DateTime.Parse(mData.DateUpdated).ToString("dd MMMM yyyy", CultureInfo.InvariantCulture);
txtItemDate.Text = mData.Text;
return convertView;
}
}
Code for listview to take full height :-
public static void SetListViewHeightBasedOnChildren(ListView listView)
{
IListAdapter listAdapter = listView.Adapter;
if (listAdapter == null)
return;
int desiredWidth = Android.Views.View.MeasureSpec.MakeMeasureSpec(listView.Width, MeasureSpecMode.Unspecified);
int totalHeight = 0;
View view = null;
for (int i = 0; i < listAdapter.Count; i++)
{
view = listAdapter.GetView(i, view, listView);
if (i == 0)
view.LayoutParameters = new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WrapContent);
totalHeight += view.MeasuredHeight;
view.Measure(desiredWidth, totalHeight);
}
ViewGroup.LayoutParams param = listView.LayoutParameters;
param.Height = totalHeight + (listView.DividerHeight * (listAdapter.Count - 1));
listView.LayoutParameters = param;
listView.RequestLayout();
}
Try the below code may be its because of divider height. Have you defined custom height for divider in your listview?.
/**
* Sets ListView height dynamically based on the height of the items.
*
* #param listView to be resized
*
*/
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
float singleViewHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
singleViewHeight = listItem.getMeasuredHeight();
}
// Get total height of all item dividers.
int totalDividersHeight = listView.getDividerHeight() *
(listAdapter.getCount() - 1);
// Set list height
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + totalDividersHeight + (Math.round(singleViewHeight / 2));
listView.setLayoutParams(params);
listView.requestLayout();
}

Get width layout when layout add view dynamically

I have a problem when I getWidth of layout... It doesn't return a real width after adding new view?... how can I solve it... thanks... This is my sample code.
llDayHeaderTable = (LinearLayout) viewGroup
.findViewById(R.id.llDayHeaderTable);
LayoutInflater vi = (LayoutInflater) parent
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (View v : vTmp) {
llDayHeaderTable.removeView(v);
}
vTmp.removeAllElements();
for (int i = 0; i < dto.arrProductTitleList.size(); i++) {
String productName = dto.arrProductTitleList.get(i).productInfoName;
vItemHeader = vi.inflate(R.layout.layout_kpi_sku_group_accum_item,
null);
TextView tv = (TextView) vItemHeader
.findViewById(R.id.tvHeaderProduct);
tv.setText("" + productName);
llDayHeaderTable.addView(vItemHeader);
vTmp.add(vItemHeader);
}
int width = llDayHeaderTable.getWidth();
Try using getMeasuredHeight() and getMeasuredWidth() instead of getWidth() and getHeight()
llDayHeaderTable.measure(0, 0);
final int w = llDayHeaderTable.getMeasuredWidth();
final int h = llDayHeaderTable.getMeasuredHeight();

Make custom visualization for ListView inside a ScrollView

I have a ScrollView which contains a lot of items. On the bottom of it I add a set of items from another Activity. I check the input ArrayList from another Activity and should to add proper count of checkbox to my Activity with ScrollView. I add items with the help of LinearLayout like that:
public void addFiles()
{
adapter = new ArrayAdapter<File>(this,
R.layout.new_order, R.id.fileCheck,
FileManagerActivity.finalAttachFiles)
{
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// creates view
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.file, null);
view.setFocusable(false);
CheckBox textView = (CheckBox) view
.findViewById(R.id.fileCheck);
textView.setClickable(true);
textView.setText(FileManagerActivity.finalAttachFiles.get(position).getName().toString());
textView.setTextColor(Color.BLACK);
textView.setPadding(55, 0, 0, 0);
textView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.file_icon, 0, 0, 0);
textView.setTextSize(16);
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
textView.setCompoundDrawablePadding(dp5);
textView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(((CheckBox)v).isChecked()){
checks.set(position, 1);
}
else{
checks.set(position, 0);
}
}
});
return view;
}
};
}
I know that using ListView inside a ScrollView is not recomended but I tried to use LinearLayout for my aim, but it worked bad - it couldn't updated properly. To display ListView in proper way I use such a class:
public class UtilityListView {
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);
Log.i("ListAdapter count",Integer.toString(listAdapter.getCount()));
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();
}
}
And use it after every data changing like that:
fileList = (ListView) findViewById(R.id.fileListView);
fileList.setAdapter(adapter);
UtilityListView.setListViewHeightBasedOnChildren(fileList);
But the height of the ListView is much bigger than quantity of items of the list...How can I fix it?
I solved the problem using the ListView with footers and headers. All the attempts to use ListView inside the ScrollView were unworkable or unefficient. I did it like this:
View header = getLayoutInflater().inflate(R.layout.header_listview, null);
View footer = getLayoutInflater().inflate(R.layout.footer_listview, null);
fileList.addHeaderView(header);
fileList.addFooterView(footer);
fileList.setCacheColorHint(Color.WHITE);
fileList.setAdapter(adapter);
All my items shows properly and I can get functionality, what I wanted.

setImageResource in GridView makes holes

Hello everyone I have a problem.
I want to do a GridView with a Custom Loyout so i used the layoutInflater and i did this:
private ImageView prima;
private ImageView seconda;
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
LayoutInflater li = getLayoutInflater();
v = li.inflate(R.layout.icon, null);
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
prima = (ImageView) v.findViewById(R.id.imageView1);
prima.getLayoutParams().height = width / 3;
prima.getLayoutParams().width = width / 3;
seconda = (ImageView) v.findViewById(R.id.imageView2);
seconda.getLayoutParams().height = width / 3;
seconda.getLayoutParams().width = width / 3;
v.setLayoutParams(new GridView.LayoutParams(width / 3, width / 3));
v.setPadding(0, 0, 0, 0);
} else {
v = convertView;
}
prima.setImageResource(mThumbIds[position]); //mThumbIds[] is an array with R.drawable.vip_0_mini, R.drawable.a_1, R.drawable.b_2, R.drawable.c_3 .....
return v;
};
When i run my application the image are arranged random and there are many black space with no images.
What i did wrong ?
Your View and convertView are not linked.
Try with ViewHolder concept like this:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder v;
if (convertView == null) {
LayoutInflater li = getLayoutInflater();
convertView = li.inflate(R.layout.icon, null);
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
v = new ViewHolder();
v.prima = (ImageView) convertView .findViewById(R.id.imageView1);
v.prima.getLayoutParams().height = width / 3;
v.prima.getLayoutParams().width = width / 3;
v.seconda = (ImageView) convertView .findViewById(R.id.imageView2);
v.seconda.getLayoutParams().height = width / 3;
v.seconda.getLayoutParams().width = width / 3;
convertView .setLayoutParams(new GridView.LayoutParams(width / 3, width / 3));
convertView .setPadding(0, 0, 0, 0);
convertView.setTag(v);
} else {
v = (ViewHolder)convertView.getTag();
}
v.prima.setImageResource(mThumbIds[position]); //mThumbIds[] is an array with R.drawable.vip_0_mini, R.drawable.a_1, R.drawable.b_2, R.drawable.c_3 .....
//v.seconda
return convertView;
};
class ViewHolder{
ImageView prima;
ImageView seconda;
}
You forgot to apply images to
seconda = (ImageView) v.findViewById(R.id.imageView2);
prima.setImageResource(....);

Categories

Resources