Search code examples
androidstacklistadapterandroid-framelayout

Stacking a view on the top of a framelayout in an arrayadapter


I have an arrayadapter that displays an array of bitmaps that it gets from a server. Up to four bitmaps can be shown at the same time on the screen, with the list cycling through them using the usual "holder" pattern. The bitmaps are contained within objects and one of their properties is a date - if the date of the bitmap object is greater than X, I want to be able to put a layer on top of that particular bitmap in the list with the word "new" with a 50% transparency.

To do this, I added a framelayout to the existing bitmap layout and I perform an addView on this, adding the "new" layer (which is in a seperate layout file), and I change a boolean flag on the bitmap object to show that I marked it as "new" at some point. If the bitmap is old and I see that it was marked as "new" at some point in the past, I change the flag to old and remove the new layer.

The problem is that when I cycle down through the list and encounter a bitmap that should be "new" it displays correctly, but when I cycle back up the list and back down, the list progressively poisons itself so that eventually all the bitmaps are shown as "new". Also, the backgrounds for the "new" layer starts out as 50% transparent and after a few movements up and down the list they are all black - indicating that it's adding the "new" layer over and over and over again to the list for every single bitmap. To make this explanation a little clearer - here is the relevant code:

Layout file for the "new" layer (R.layout.new_over_layer):

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/half_transparent"
android:gravity="top|center_horizontal"
>

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="100dp"
    android:src="@drawable/new_icon"
    android:background="@color/transparent"
    />

Layout file for every bitmap

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
android:id="@+id/newlistview_framelayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:foregroundGravity="top|center_horizontal"
>

<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="180dp"
android:layout_marginBottom="4dp"
android:background="@color/white" >



<ImageView
    android:id="@+id/some_bitmap"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center"
    android:layout_marginBottom="4dp"
    android:contentDescription="@string/some_bitmap_desc"
    android:scaleType="fitXY"
    android:src="@color/black" />

</RelativeLayout> </FrameLayout>

Relevant code section for the adapter

public class myCustomAdapter extends ArrayAdapter<BitmapObject>
{
public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    View currentView = convertView;

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    View newBitmapLayer = inflater.inflate(R.layout.new_over_layer,parent,false);

    getCurrentTime();

    if (isBitmapNew(bitmapList.get(position))) { 

        if (!bitmapList.get(position).getNew()) {
            bitmapList.get(position).setNew(true);
            ((FrameLayout)     currentView.findViewById(R.id.newlistiview_framelayout)).addView(newBitmapLayer);
        }
    } else { 

        if (bitmapList.get(position).getNew()) { //check if marked new before and reset to old
            bitmapList.get(position).setNew(false);
            ((FrameLayout)     currentView.findViewById(R.id.newlistiview_framelayout)).removeView(newBitmapLayer);
        }
    }

    if (null == currentView) {

        currentView = inflater.inflate(R.layout.new_listiview_item, parent, false);

        holder = new ViewHolder();
        holder.bitmapName = (TextView) currentView.findViewById(R.id.bitmap_name);
        currentView.setTag(holder);

    } else {
        holder = (ViewHolder) currentView.getTag();
    }

....
}

So.. why is the list poisoning itself when I scroll up and down, and how do I change the code so that it functions correctly and only adds "new" once to every bitmap that needs to be marked as new?


Solution

  • Solved by completely changing the way of doing this.. instead of trying to add and remove layers from the list, I created the layer with the transparent "new" as a permanent sublayer in the framelayout. Then, whenever the bitmap was new I used bringchildtofront on the "new" layer, and whenever the bitmap wasn't new I used bringchildtofront on the bitmap layer (which has an opacity of 100% and thus hides the "new" layer).

            if (isBitmapNew(bitmapList.get(position))) { 
    
            ((FrameLayout) currentView.findViewById(R.id.newlistiview_framelayout)).bringChildToFront(currentView.findViewById(R.id.bitmap_new));
    
        } else {
    
            ((FrameLayout) currentView.findViewById(R.id.newlistiview_framelayout)).bringChildToFront(currentView.findViewById(R.id.bitmap_old));
        }
    

    Because I'm never adding or removing layers the list works much faster and can't be poisoned.