3

Ok, my problem is similar to this Background image in ListView entries spontaneously disappearing

but this issue is resolved with using setImageResource on a normal ImageView but I want an animation to exist on all elements of a list, and this can not be done without a custom ImageView class that allows the animation to run. Does anyone know why these images are disappearing after scrolling in the list view and maybe how this can be fixed? Thanks a lot!

Here is my ListAdapter:

    package com.mmgworldwide.layout.listadapters;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import com.mmgworldwide.R;
import com.mmgworldwide.customviews.SpinLoadingAnim;
import com.mmgworldwide.data.FWREvent;
import com.mmgworldwide.data.ImageThreadLoader;
import com.mmgworldwide.data.ImageThreadLoader.ImageLoadedListener;
import com.mmgworldwide.layout.ListDivider;
import com.mmgworldwide.data.Personality;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class PersonalityListAdapter extends ArrayAdapter<Object>{

    private Context parentContext;
    public ArrayList<Object> items_list_ref;
    private ImageThreadLoader imageLoader = new ImageThreadLoader();

    public PersonalityListAdapter(Context context, int resource, int textViewResourceId, ArrayList<Object> items_list) {
        super(context, resource, textViewResourceId, items_list);
        items_list_ref = items_list;
        parentContext = context;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Object node = (Object) items_list_ref.get(position);
        LayoutInflater inflater = (LayoutInflater) parentContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if(node instanceof ListDivider){
            //make new div bar
            View div = inflater.inflate(R.layout.list_bar, parent, false);
            TextView div_label = (TextView) div.findViewById(R.id.div_label);
            div_label.setText(((ListDivider) node).label.toUpperCase());
            return div;
        }

        View row = inflater.inflate(R.layout.picture_list, parent, false);

        TextView name_txt = (TextView) row.findViewById(R.id.name_txt);
        TextView title_txt = (TextView) row.findViewById(R.id.title_txt);
        final ImageView portrait_img = (ImageView) row.findViewById(R.id.portrait);
        final ImageView load_cnt = (ImageView) row.findViewById(R.id.portrait_loadDisplay);

        name_txt.setText(((Personality) node).getName());
        SpannableString title_underlined = new SpannableString(((Personality) node).getTitle());
        title_underlined .setSpan(new UnderlineSpan(), 0, title_underlined.length(), 0);
        title_txt.setText(title_underlined);

        load_cnt.setVisibility(View.VISIBLE);

        return row;
    }

Here is my custom ImageView class

package com.mmgworldwide.customviews;

import android.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;

public class SpinLoadingAnim extends ImageView{

    AnimationDrawable animation;
    private final Integer FRAME_SPEED = 20;

    public SpinLoadingAnim(Context context, AttributeSet attrs){
        super(context, attrs);

         //setBackgroundColor(Color.GREEN);


        animation = new AnimationDrawable();
        animation.addFrame(context.getResources().getDrawable(com.mmgworldwide.R.drawable.spinner_00000), FRAME_SPEED);
        animation.addFrame(context.getResources().getDrawable(com.mmgworldwide.R.drawable.spinner_00001), FRAME_SPEED);
        animation.addFrame(context.getResources().getDrawable(com.mmgworldwide.R.drawable.spinner_00002), FRAME_SPEED);
        animation.addFrame(context.getResources().getDrawable(com.mmgworldwide.R.drawable.spinner_00003), FRAME_SPEED);

        animation.setOneShot(false);

        this.setBackgroundDrawable(animation);
        this.post(new Starter());
    }

    class Starter implements Runnable {
        public void run() {
            animation.start();

        }

    }

And here is my layout XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="#FFFFFF"
  android:gravity="center_vertical"
  android:paddingTop="10dp"
  android:paddingBottom="10dp"
  android:paddingLeft="10dp"
  android:paddingRight="10dp">
    <ImageView android:layout_width="60dp" android:layout_height="60dp" android:id="@+id/portrait" android:layout_centerVertical="true"></ImageView>


    <com.mmgworldwide.customviews.SpinLoadingAnim
        android:id="@+id/portrait_loadDisplay" 
        android:layout_width="20dp" android:layout_height="20dp"
        android:background="#939598"
        android:layout_centerVertical="true" android:layout_marginLeft="20dp"
        android:layout_alignParentBottom="true" 
        android:layout_alignParentLeft="true" />



    <ImageView android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_centerVertical="true" android:id="@+id/arrow" android:src="@drawable/list_arrow" android:layout_alignParentRight="true"></ImageView>
    <LinearLayout android:layout_toRightOf="@id/portrait" android:layout_toLeftOf="@id/arrow" android:layout_centerVertical="true" android:orientation="vertical" android:layout_height="wrap_content" android:layout_width="wrap_content" android:baselineAligned="true" android:paddingLeft="15dp" android:paddingRight="15dp">
        <TextView android:text="CHEF NAME" android:id="@+id/name_txt" android:layout_width="wrap_content" android:textColor="#000000" android:textStyle="bold" 
            android:layout_height="wrap_content" android:paddingBottom="2dp" android:textSize="15sp"></TextView>
        <TextView android:text="Chef Title" android:id="@+id/title_txt" android:layout_width="wrap_content" android:textColor="#000000" 
            android:layout_height="wrap_content" android:textSize="13sp"></TextView>
    </LinearLayout>


</RelativeLayout>

Every 5th 'preloader' graphic is still disappearing, but thanks Michele this has helped a lot, and made things cleaner.

package com.mmgworldwide.layout;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.mmgworldwide.R;
import com.mmgworldwide.data.DataConnection;
import com.mmgworldwide.data.ImageThreadLoader;
import com.mmgworldwide.data.Personality;

//http://code.google.com/p/and-conference/source/browse/trunk/src/com/totsp/conference/PresentationList.java

public class Personalities extends Activity{

    private ListView listView;
    private ArrayAdapter<Object> adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.personalities);

        listView = (ListView) findViewById(R.id.personalitylistview);
        listView.setDrawingCacheEnabled(false);


        addListAdapter();

        setFooterView("more");
    }

    private void addListAdapter(){
        ArrayList<Object> adaptList = makeAdaptList();

        adapter = new PersonalityListAdapter(this, 0, adaptList);
        listView.setAdapter(adapter);
    }

    private ArrayList<Object> makeAdaptList(){
        ArrayList<Object> adaptList = new ArrayList<Object>();
        String currentDivDate = " ";
        for(Personality aEvent : DataConnection.fwrInfo.getPersonalityList()){
            adaptList.add(aEvent);
        }

        return adaptList;
    }

    private void setFooterView(String highlight){
        Footer footer = (Footer) findViewById(R.id.footer);
        footer.setHighlight(highlight);
    }

    //***********************************************************************************************************************************************
    //ViewHolder
    //***********************************************************************************************************************************************
    static class ViewHolder {
        private TextView tView1;
        private TextView tView2;
        private ImageView iView1;
    }

    //***********************************************************************************************************************************************
    //PersonalityListAdapter
    //***********************************************************************************************************************************************
    private class PersonalityListAdapter extends ArrayAdapter<Object>{

        LayoutInflater vi = (LayoutInflater) Personalities.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        private Context parentContext;
        public ArrayList<Object> items_list_ref;
        private ImageThreadLoader imageLoader = new ImageThreadLoader();

        public PersonalityListAdapter(final Context context, final int resId, final ArrayList<Object> presentations) {
            super(context, resId, presentations);
            this.items_list_ref = presentations;
            parentContext = context;
        }

        @Override
    public int getCount() {
        return items_list_ref.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //Log.d("TROY", "adding view");
            View v = convertView;

            if (v == null) {
                //Log.d("TROY", "inflation");
                v = vi.inflate(R.layout.picture_list, parent, false);
                ViewHolder viewHolder = new ViewHolder();

                viewHolder.tView1 = (TextView) v.findViewById(R.id.name_txt);
                viewHolder.tView2 = (TextView) v.findViewById(R.id.title_txt);
                viewHolder.iView1 = (ImageView) v.findViewById(R.id.portrait);

                v.setTag(viewHolder);
            }

            Object p = items_list_ref.get(position);

            if (p != null) {
                //Log.d("TROY", "setting stuff");
                ViewHolder viewHolder = (ViewHolder) v.getTag();
                viewHolder.tView1.setText("hi");
            }

            return v;
        }
    }

}

Here is a screen shot of what's going on (why is the 5th preload graphic not showing?):

enter image description here

edit ------

Ok, I added the getCount and getItemId overrides but still have the same issues, I figured I would start a project from scratch and try again with just a listView, same issue, I have put up a test project to demonstrate this frustrating issue. In this test I did not even add any data to the adapters, and the animation only works for some of them, just like with my master project.

Thank you so much for your patience and help this far Michele, you have been very awesome.

http://faceonfire.com/temp/ListTester.zip

Community
  • 1
  • 1
troy
  • 496
  • 5
  • 6
  • hm can you test it with a static .png image? and can you provide the xml for com.mmgworldwide.customviews.SpinLoadingAnim – Michele Sep 15 '11 at 08:25
  • Static PNG does work fine, it's only when I add animation that some do not work the SpinLoadingAnim is above under 'Here is my custom ImageView class' (sorry for the bad labeling) – troy Sep 15 '11 at 13:04
  • I have noticed, that the one's that do not show up are when the ViewHolder first stops inflating the 'picture_list' adapter, and after that no inflation occurs but the animation that did not show still does not show no matter how much scrolling and it seems to be persistent, for example if the 7th adapter is not showing then it will always not show given the resolution and the length of the list. Not sure that helps but there has to be some clue here that will help me solve this weird issue. – troy Sep 15 '11 at 13:22
  • 1
    hm is the SpinAnimation cached inside your ViewHolder? cant find portrait_loadDisplay, anyway i edited my Answer, give it a try. – Michele Sep 15 '11 at 13:28
  • Ok, I changed the animation so it's inside XML, and because it's a resource I am not longer seeing the graphic disappear, but now the animation freezes on those same one's before where it didn't show :( it has to be a memory or recycling issues but I have no clue where it's coming from. – troy Sep 16 '11 at 16:28
  • i edited my anwser, give it a try :) – Michele Sep 16 '11 at 16:47
  • Uploaded a working example at http://faceonfire.com/temp/ListTester.zip that demonstrates the issue, I am still at a loss about this particular issue. – troy Sep 20 '11 at 13:54

1 Answers1

2

ListViews are highly optimized Datastructures. You should use ViewHolders to obey the rules of Lists and make the life of the system a bit easier.

When you Scroll a ListView the System caches the Items in Holders for performance. Read How to load the Listview "smoothly" in android

you can also try to set

yourListView.setDrawingCacheEnabled(false);

edit: ok than try to set your animation with xml

http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

like:

<!-- Animation frames are wheel0.png -- wheel5.png files inside the
 res/drawable/ folder -->
 <animation-list android:id="selected" android:oneshot="false">
    <item android:drawable="@drawable/wheel0" android:duration="50" />
    <item android:drawable="@drawable/wheel1" android:duration="50" />
    <item android:drawable="@drawable/wheel2" android:duration="50" />
    <item android:drawable="@drawable/wheel3" android:duration="50" />
    <item android:drawable="@drawable/wheel4" android:duration="50" />
    <item android:drawable="@drawable/wheel5" android:duration="50" />
 </animation-list>

edit2: please try to implement getCount and getItemId, theres something wrong with the boundarys of your List, maybe android cant find out on with position the list is after scrolling. Oh and can you please test it with random data, not the same item all the way (perhaps some weird hash (default getItemId Implementation?) that is always the same with the same item content). for example like this:

@Override
public int getCount() {
    return <yourdatastructure>.getSize();
}

@Override
public long getItemId(int position) {
    return position;
}
Community
  • 1
  • 1
Michele
  • 6,126
  • 2
  • 41
  • 45
  • Ok, this makes sense and I have implemented a ViewHolders template and it has helped a lot. I also added setDrawingCacheEnabled(false)... and now less of my preloading graphics disappear, but every 5th one for some reason still does. I will show my new code below. – troy Sep 14 '11 at 20:17