ListView not showing images properly - android

i have a listview that shows images downloaded from internet and have a custom layout for each row of the listview as below :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#00f"
>
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="#drawable/wish"
android:gravity="center_vertical|center_horizontal"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:text="" />
</LinearLayout>
My getview method is as belows :
public View getView(int arg0, View arg1, ViewGroup arg2)
{
// TODO Auto-generated method stub
View v;
final TextView tv;
if(arg1 == null)
{ // if it's not recycled, initialize some attributes
LayoutInflater lf = (LayoutInflater) conn.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = lf.inflate(R.layout.layitem, arg2, false);
tv = (TextView) v.findViewById(R.id.textView1);
tv.setLayoutParams(new LinearLayout.LayoutParams(width, width / 2));
// tv.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.CENTER_VERTICAL);
}
else
{
v = arg1;
tv = (TextView) v.findViewById(R.id.textView1);
}
if(arg0 < count)
{
Bitmap object = getImage(iurl[arg0]);
tv.setBackground(new BitmapDrawable(conn.getResources(), object));
tv.setText(texts[arg0]);
}
else
{
tv.setBackgroundResource(R.drawable.wish);
tv.setText("");
}
return v;
}
What i am trying to do is that my ListView has 20 rows and my no of records is less than 20.so the records fill up the rows and the rest have default background image.count is the actual no of records.
iurl is the array containing the name of images to obtain from server and texts is an array containing messages.
The problem is that arg0 does not go beyond 2 and all rows contain images indexed from 0 to 2 and when i scroll down that it changes image corresponding to the row number.
Can i put the correct image in the correct row at the first stance or it will only take up as it scrolls.
i am setting the width of the textview as per the width of the screen of the mobile.is it the right way to do .kindly update.

I think you should use an asyntask to get images from specific url on internet.
If you don't use thread to load images from internet, it will take a long time to load image and crash your programe

Have you overriden getCount() method? It should return the number of items that are in the data set represented by this Adapter.

Related

Android GridView Custom Adapter only ever displays a single row

I am working on a ListView with items that can expand to show a GridView. The ListView and adapter work fine, and the GridView expands fine when added, but I cannot get it to display more than a single row in the grid. There is only ever a single column displayed, no matter the length I set the column to, and it's clear that the issue isn't that the view is just too small (I have tried making it have an absurdly large height). I've browsed other questions here but this doesn't seem to be a common problem. What am I doing wrong?
xml:
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scorecardGridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="25dp"
android:numColumns="18"
android:isScrollContainer="false"
android:verticalSpacing="5dp"
android:horizontalSpacing="5dp"
android:stretchMode="columnWidth"
android:gravity="center" />
GridAdapter getView() code:
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("position",String.valueOf(position));
TextView textView;
if (convertView == null) { // if it's not recycled, initialize some
// attributes
textView = new TextView(context);
textView.setLayoutParams(new GridView.LayoutParams(25, 25));
} else {
textView = (TextView) convertView;
}
if (position < 18)
textView.setText(String.valueOf(position+1));
else {
position -= 18;
textView.setTypeface(null, Typeface.BOLD);
if (scores[position] > 0) {
textView.setTextColor(context.getResources().getColor(
R.color.red));
textView.setText("+" + String.valueOf(scores[position]));
} else {
if (scores[position] < 0)
textView.setTextColor(context.getResources().getColor(
R.color.blue));
textView.setText(String.valueOf(scores[position]));
}
}
return textView;
}
I am using this to show a two row, 18 column golf scorecard. No matter what I do, only the first row shows. If I change the numColumns attribute, that new number of columns shows, but always just one row. You can see I tried Logging the positions for which getView() is being called, and it is revealing that it is only being called for the first row.
Let me know if you want to see any other code, thanks.

Image resets to default after scrolling - Android Custom View

So I've been creating a custom view similar to GridView. It loads and scrolls through images just fine when they are resources inside the app but now that I'm using images coming in through an HTTP request, the images aren't loading correctly.
When the app starts: all images are set to the default (bad)
After scrolling past that cell and immediately scrolling back to it: image loads correctly (good)
After scrolling back to that same cell sometime later: image was set back to the default (bad)
Does anyone have any ideas of what could be causing this error? I assume it's some kind of recycling issue but I haven't been able to fix it.
Here is my xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:paddingBottom="1dip"
android:background="#color/white"
android:id="#+id/highlight_counter_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:gravity="center" >
<ImageView
android:id="#+id/catalog_image"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:id="#+id/solid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/black" />
<View
android:id="#+id/text_gradient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#id/solid"
android:background="#drawable/highlight_text_gradient" />
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:padding="#dimen/highlight_text_padding"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="2"
android:textColor="#color/white"
android:textSize="#dimen/text_large" />
</RelativeLayout>
Here is an excerpt from my adapter (where I think the problem probably lies):
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final LayoutParams lp;
int viewType = getItemViewType(position);
ImageView img;
PulseTextView title;
Resources res = getContext().getResources();
int height = (int) res.getDimension(R.dimen.icon_main_size);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.element_item, parent, false)
LayoutParams layp = new LayoutParams(height);
convertView.setLayoutParams(layp);
}
img = ViewHolder.get(convertView,R.id.catalog_image);
title = ViewHolder.get(convertView,R.id.title);
final CatalogItem channel = getCatalogItem(position);
// find the url of the associated image then set image
String url = null;
try {
url = mCatalogHandler.getImageUrl(CatalogHandler.VALUE_ICON, channel.mPrimaryKey, 100, 100);
} catch (CatalogException e) {
e.printStackTrace();
}
if (url == null || TextUtils.isEmpty(url) || url.equals("null")) {
img.setImageBitmap(mDefaultPic);
} else {
// downloads the image to img
mImageDownloader.download(url, img, mDefaultPic, false);
}
title.setText(channel.mDomain);
img.setScaleType(ScaleType.FIT_XY);
img.setTag(RAMImageCache.KEY_URL, url);
// set the gradient behind the text
View grad = convertView.findViewById(R.id.text_gradient);
ViewUtils.setHeight(grad, height * 3 / 5);
grad.getBackground().setDither(true);
View solid = convertView.findViewById(R.id.solid);
ViewUtils.setHeight(solid, height / 5);
// set the padding based on the position on the screen
DisplayMetrics displaymetrics = new DisplayMetrics();
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
if (convertView.getRight() == width && convertView.getLeft() == 0) {
convertView.setPadding(0, 0, 0, 1);
} else if (convertView.getRight() == width) {
//convertView.setPadding(1, 0, 0, 1);
convertView.setPadding(0, 0, 1, 1);
} else if (convertView.getLeft() == 0) {
//convertView.setPadding(0, 0, 1, 1);
convertView.setPadding(1, 0, 0, 1);
}
// set the onclicklistener to jump to the next fragment
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString("channelitem", channel.getMetadata().toString());
ChannelFragment fragment = new ChannelFragment();
fragment.setArguments(bundle);
((PulseFragmentActivity)mContext.get()).openFragment(fragment);
}
});
return convertView;
}
static class ViewHolder {
ImageView img;
TextView title;
public ViewHolder(ImageView i, PulseTextView t) {
img = i;
title = t;
}
public static <T extends View> T get(View view, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}
Any point in the right direction would help greatly! Let me know if there are any other code snippets you would need to see.
I suggest one way to debug is to try another image download library to find out if the error is in your code. I used https://github.com/koush/UrlImageViewHelper and it works well when cell is reused, and its API is similar to what you used now
The behaviour very much depends on what and how does your mImageDownloader handles the downloaded image.
In most cases LRU Cache implementation might be used to store your downloaded image and this cache has a maximum value of bytes assigned. Once your cached image exceeded this value, the old bitmaps will be discarded, hence why you are seeing default image and you need to re-download it.
My suggestion is after you have downloaded the image, scale it as small as you can, so that you can cache as much bitmaps as you could.
There is no way Android could cache up all your downloaded images due to memory restriction.

Android - ListView not updating barprogress (sometimes)

I have an xml layout which I use for each row in my listview:
<TextView
android:id="#+id/bar"
android:layout_width="fill_parent"
android:layout_height="20dip"
android:layout_marginTop="10dip"
android:layout_below="#+id/user_name"
android:background="#drawable/userchallenge_layout_border"/>
<TextView
android:id="#+id/bar_reached"
android:layout_width="wrap_content"
android:layout_height="20dip"
android:layout_marginTop="10dip"
android:layout_below="#+id/user_name"
android:background="#drawable/userchallenge_layout_border_progress"/>
The background is a drawable I made myself in an xml file, with color, strokes and such.
I have a list of users in my listview, and each user has a value, and I want to draw the as a progress how far each user has reached in the challenge. This is how I calculate it and draw it in my listview:
TextView userName = (TextView) v.findViewById(R.id.user_name);
userName.setTypeface(tf);
userName.setText(uc.getUser().getFullName());
TextView totalBar = (TextView) v.findViewById(R.id.bar);
final int totalBarWidth = totalBar.getWidth();
TextView percent = (TextView) v.findViewById(R.id.percent);
percent.setTypeface(tf);
percent.setText(oneDigit.format((uc.getValue()/challenge.getGoal()) * 100) + " %");
TextView reachedBar = (TextView) v.findViewById(R.id.bar_reached);
reachedBar.setWidth((int) ((uc.getValue()/challenge.getGoal()) * totalBarWidth));
I get the right calculation, but my TextView (bar_reached) sometimes sets the width and sometimes it doesn't, I know that listviews redraws each time for each row, but I don't know how to solve this problem!
Please help
You need to make a List<Float> progressList of progress and load data from it progressList.get(position) when getView(int position, View convertView, ViewGroup parent) method is called . When progress changed save it progressList.get(position) = newProgress
By the way why just not to use ProgressBar?

Android findViewById on dynamically created textviews in custom list adapter does not seem to work

I'm building a configuration program which prints out value/item lists dynamically. Sometimes you will have one item to output, for example, a name, but other times you might have multiple items that you want outputted, for example, an ip address, username, password, etc.
The idea is to build up a list dynamically depending on the amount of parameters outputted. On a small screen the amount of parameters might be 2, but in landscape mode or on a tablet the amount of parameters might be much higher.
I have this working when I have a hard coded XML file with Android IDs, i.e. referring to
android:id="#+id/item1
but I'm struggling with doing this programmatically. Before the edit I was doing this with IDs but on advice I am now rather trying this with tags.
Here are all the relevant parts of the code. Is a multi-line list of parameters is outputted we branch to buildLayout to build up the custom layout:
public void processAsyncTask(String result) {
ConfigList list = getConfigList(result);
ArrayList<ConfigCollection> mParamsList = list.getAllItems();
// Once we have a list of parameters we build the layout according to size
buildLayout(mParamsList.size());
mMultiRowAdapter = new ParamsAdapter(this, R.layout.multi_row_config, mParamsList);
mCollectionListView.setAdapter(mMultiRowAdapter);
}
Here is buildLayout:
private void buildLayout(int size) {
TableRow tr = (TableRow)findViewById(R.id.row_multi_row);
for (int i = 0; i < size - 1; i++) {
TextView tv = new TextView(this);
tv.setId(i);
tv.setTag("id" + String.valueOf(i));
tv.setTextAppearance(this, android.R.style.TextAppearance_Large);
tv.setText("id" + String.valueOf(i));
tr.addView(tv);
}
}
And here is how I am trying to refer to the IDs:
/**
* Custom Array Adapter with a view that displays multi-line item/value pairs
*/
private class ParamsAdapter extends ArrayAdapter<ConfigCollection> {
private ArrayList<ConfigCollection> objectList;
public ParamsAdapter(Context context, int textViewResourceId, ArrayList<ConfigCollection> objectList) {
super(context, textViewResourceId, objectList);
this.objectList = objectList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.multi_row_config, null);
// It works with hard coded IDs, e.g.:
TextView tv1 = (TextView) v.findViewById(R.id.item1);
TextView tv2 = (TextView) v.findViewById(R.id.item2);
// BELOW DOES NOT, tv1 and tv2 is null after assignment
TextView tv1 = (TextView) v.findViewWithTag("id0");
TextView tv2 = (TextView) v.findViewWithTag("id1");
Here is the XML file that I am using to test with:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
android:id="#+id/table_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="#android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TableRow
android:id="#+id/row_multi_row"
>
<TextView
android:id="#+id/item1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="item1"
android:padding="3dip"
/>
<TextView
android:id="#+id/item2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="item2"
android:padding="3dip"
/>
</TableRow>
</TableLayout>
EDIT:
I've isolated the problem further visible here with screenshots. It seems in buildLayout I am adding textviews but in the custom array adapter the layoutinflator does not find these fields. I've also updated the code to try and work with tags instead of IDs.
Debug from buildLayout. Notice four children, the two fixed in the XML code and two created
Debug from ParamsAdapter. Notice only two children at this stage where I am actually trying to access these objects.
If you are creating items programmatically, I would advise using the "tag" parameter. It's not as fast as id, but then you are not worried about speed for this - as long as you have the parent view you can "findViewWithTag" - see http://developer.android.com/reference/android/view/View.html - scroll down to findViewWithTag

Detect whether TextView in ListView is ellipsized

I have a custom Adapter that renders some items in a ListView. I need to show an icon on the ListView's items, if the item's text is ellipsized, and hide it if there's enough room for the text to finish. I have access to the button in getView method of my adapter (where I set the text) but the ellipses are not added immediately upon setting the text.
Is there any way I can do this?
Here's my TextView markup:
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:id="#+id/list_item_description"/>
public int getEllipsisCount (int line):
Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.
So, simply call :
if(textview1.getLayout().getEllipsisCount() > 0) {
// Do anything here..
}
Since the getLayout() cant be called before the layout is set, use ViewTreeObserver to find when the textview is loaded:
ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Layout l = textview.getLayout();
if ( l != null){
int lines = l.getLineCount();
if ( lines > 0)
if ( l.getEllipsisCount(lines-1) > 0)
Log.d(TAG, "Text is ellipsized");
}
}
});
And finally do not forget to remove removeOnGlobalLayoutListener when you need it nomore.
The tricky part is that the view you're working with in getView will sometimes have been laid out, and sometimes not, so you have to handle both cases.
When it hasn't been laid out, you set a view tree observer to check on the ellipsis once it has been. In the case of recycled views, the layout will already be there and you can check for the ellipsis immediately after setting the text.
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder vh;
if (convertView == null) {
vh = new ViewHolder();
... // create/inflate view and populate the ViewHolder
}
vh = (ViewHolder) convertView.getTag();
// Set the actual content of the TextView
vh.textView.setText(...);
// Hide the (potentially recycled) expand button until ellipsizing checked
vh.expandBtn.setVisibility(GONE);
Layout layout = vh.textView.getLayout();
if (layout != null) {
// The TextView has already been laid out
// We can check whether it's ellipsized immediately
if (layout.getEllipsisCount(layout.getLineCount()-1) > 0) {
// Text is ellipsized in re-used view, show 'Expand' button
vh.expandBtn.setVisibility(VISIBLE);
}
} else {
// The TextView hasn't been laid out, so we need to set an observer
// The observer fires once layout's done, when we can check the ellipsizing
ViewTreeObserver vto = vh.textView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Layout layout = vh.textView.getLayout();
if (layout.getEllipsisCount(layout.getLineCount()-1) > 0) {
// Text is ellipsized in newly created view, show 'Expand' button
vh.expandBtn.setVisibility(VISIBLE);
}
// Remove the now unnecessary observer
// It wouldn't fire again for reused views anyways
ViewTreeObserver obs = vh.textView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});
return convertView;
}
I hope I understand your question correctly--if you're looking to end a TextView that's too wide for a screen with ellipses, you can add these attributes to your TextView:
android:ellipsize="end"
android:maxLines="1"
android:scrollHorizontally="true"
If, however, you want to determine whether a TextView is ended with an ellipsis or is displayed fully, I'm not so sure that's possible--it doesn't look like it is. Still, you might want to try the getEllipsize() method of TextView. I'm not sure whether that returns the point at where the TextView is ellipsized by Android, or where you have set the TextView to be ellipsized.
You can either set your text to marque.
add this in your code might help.....
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:scrollHorizontally="true"
android:freezesText="true"
android:marqueeRepeatLimit="marquee_forever"
You can also put a horizontal scrollview for you code....
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/list_item_description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</HorizontalScrollView>

Categories

Resources