Android - ListView not updating barprogress (sometimes) - android

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?

Related

Parse RelativeLayout, Set Content, And Add It To A LinearLayout

Ok... here's my situation.
I have a carousel of images in a HorizontalScrollView - which contains a LinearLayout - in my Activity, like so:
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/slider"
android:scrollbars="none" >
<LinearLayout
android:id="#+id/carousel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>
I have a TypedArray, loop through it, and on each run, set these images programatically, add a ClickListener and a Tag, and add this ImageView to the LinearLayout (set in my Activity Layout), like so:
// Get the array
final TypedArray carouselArray = getResources().obtainTypedArray(R.array.carousel_array);
// Populate the Carousel with item
for (int i = 0 ; i < carouselArray.length() ; ++i) {
// Image Item
ImageView outerImage;
// Set the image view resource
if(i == 0) {
outerImage.setImageResource(R.drawable.toy_filter_clear);
}
else {
outerImage.setImageResource(carouselArray.getResourceId(i, -1));
}
// Set Touch Listener
outerImage.setOnTouchListener(this);
final String prepend = "CAROUSEL_";
final String index = String.valueOf(i);
final String tag = prepend.concat(index);
outerImage.setTag(tag);
/// Add image view to the Carousel container
mCarouselContainer.addView(outerImage);
}
But now, I just found out that I have to programatically add a second image to sit inside/on top of the first image at particular coordinates (damn you UI ppl!). I need these to be considered the same image/view essentially, so need to pack them together inside of a layout, I am assuming. So I have made a layout file, like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/carousel_item"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/carousel_outer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/toy_filter_normal"
/>
<ImageView
android:id="#+id/carousel_inner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/carousel_outer"
android:layout_alignRight="#+id/carousel_outer"
android:layout_marginBottom="10dp"
android:layout_marginRight="5dp"
android:src="#drawable/thumb_nofilter"
/>
</RelativeLayout>
This has the proper positioning, and the default images set on it. So what I want to be able to do is to reach into the Layout file, grab the ImageViews by their ID, overwrite the image if necessary, and then add that RelativeLayout to my LinearLayout at the end of my loop... sounds easy enough, right ?
My first attempt was to do it like this :
RelativeLayout item = (RelativeLayout) findViewById(R.id.carousel_item);
ImageView outerImage = (ImageView) item.findViewById(R.id.carousel_outer);
ImageView innerImage = (ImageView) item.findViewById(R.id.carousel_inner);
... but that gives me a NullPointer on the ImageView...So then I tried to inflate the RelativeLayout first, like this:
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.carousel_item_layout, null);
ImageView outerImage = (ImageView) view.findViewById(R.id.carousel_outer);
ImageView innerImage = (ImageView) view.findViewById(R.id.carousel_inner);
This gets rid of the NPE's, and (apparently) let's the images be set properly like so:
if(i == 0) {
outerImage.setImageResource(R.drawable.toy_filter_clear);
innerImage.setImageResource(0);
}
else {
outerImage.setImageResource(R.drawable.toy_filter_normal);
innerImage.setImageResource(carouselArray.getResourceId(i, -1));
}
but when I try to add the outerImage ImageView back to the LinearLayout, I get an NPE there:
mCarouselContainer.addView(outerImage);
More to the point, I don't want to add ONLY the one ImageView to the LinearLayout/HorizontalScrollView - I want to somehow pack the resulting images back into the RelativeLayout and add the whole thing back into my LinearLayout... but, it is worth mentioning, that this also gives me an NPE.
What is a guy to do ? Any thoughts appreciated...
Ok... Wow, thanks SO Code Monkey!
I managed to fix this with a one line fix, by adding the inflated View to the LinearLayout instead of the ImageView or the RelativeLayout (which wasn't doing anything), like so:
mCarouselContainer.addView(view);
Don't know why I hadn't tried that before, but I was unclear on whether as it's children were being updated if it would reflect the parent, so to speak... now I know it was.
I'm gonna keep the question up, as I think it's helpful... ?

Prevent GridView reset scroll to top when element is tapped?

I have a GridView that is populated by all apps installed on the device. The user can select certain apps here. I want the selected apps to be opaque and non-selected to be partially transparent. I did this with the following:
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setGravity(Gravity.CENTER_HORIZONTAL);
LinearLayout.LayoutParams layoutParamsText = new LinearLayout.LayoutParams(150, 90);
ImageView imageView = new ImageView(mContext);
TextView appLabel = new TextView(mContext);
final OurAppInfo info = (OurAppInfo) getItem(position);
if(!installedApplications.contains(info)){
AlphaAnimation alpha = new AlphaAnimation(0.4F, 0.4F);
alpha.setDuration(0);
alpha.setFillAfter(true);
linearLayout.startAnimation(alpha);
}
String appName = info.label;
if (appName.length() > 25) {
appName = appName.substring(0, 25);
appName = appName + "...";
}
appLabel.setText(appName);
appLabel.setTextColor(Color.BLACK);
appLabel.setGravity(Gravity.CENTER_HORIZONTAL);
appLabel.setTypeface(null, Typeface.BOLD);
imageView.setImageDrawable(info.drawableAppIcon);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(110, 110));
appLabel.setTextSize(15);
linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (installedApplications.contains(info)){
installedApplications.remove(info);
receiveUpdate(installedApplications, false, false);
} else {
installedApplications.add(info);
Collections.sort(installedApplications);
receiveUpdate(installedApplications, false, false);
}
}
});
appLabel.setLayoutParams(layoutParamsText);
linearLayout.addView(imageView);
linearLayout.addView(appLabel);
return linearLayout;
}
This is part of the GridAdapter extends BaseAdapter. The code works as expected, when I tap on an app it is either removed from or added to the list and according to transparency is set. However, whenever I tap on an element in the GridView, the view is reset and I am brought to the top of the scrollable GridView. Obviously, this isn't a problem for a small number of apps, but if you're selecting apps near the XYZ letters, every time you select one you are brought back to ABC. How can I prevent this from happening?
It looks like you're refreshing the adapter whenever you make changes that makes the grid go back to initial position. You could try saving and restoring the position before making any changes to the adapter.
//Before refreshing the adapter you get both X and Y position
int xPos = grid.getScrollX();
int yPos = grid.getScrollY();
Then you update your adapter.
After the adapter is recreated you restore the grid position:
grid.scrollTo(xPos, yPos);
You could also use (everytime possible) the method notifyDataSetChanged() instead of creating a new adapter.
Hope it helps.
Check all child views for automatic height or width.
I guess gridview calculates size of this views whenever you change data.
This was solution in my case.
In my case changed this:
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
To this:
<ImageView
android:id="#+id/image"
android:layout_width="100dp"
android:layout_height="100dp" />

ListView not showing images properly

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.

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