Weird animation on Gallery when a invalidate is re

2019-02-18 17:17发布

问题:

This is the adapter of my Gallery on which I display ImageViews (thumb of a page)

I want to load the images ASync (some times this can come from Network), so I did that code:

    public View getView(int position, View convertView, ViewGroup parent) {
        final ImageView image = convertView != null ? convertView : new ImageView(getContext());

        final PageInfo page = getItem(position);

        image.setBackgroundColor(Color.WHITE);

        image.setLayoutParams(new Gallery.LayoutParams(96, 170));

        new AsyncTask<Void, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Void... arg0) {
                try {
                    File thumbnail = page.thumbnail();//Thumbnail download the image if not available
                    return BitmapFactory.decodeStream(new FileInputStream(thumbnail));
                } catch (ApplicationException e) {
                    return null;
                } catch (NetworkException e) {
                    return null;
                }
            }

            protected void onPostExecute(Bitmap result) {
                if (result == null)
                    return;

                image.setImageBitmap(result);
            };
        }.executeOnExecutor(executor);

        return image;
    }

This works, but if I'm dragging the Gallery the views (when switching to the real Bitmap) do a "jump" to another position inside the galley, making it weird.

How can I avoid that? Or can I change the image of a ImageView without requesting layout of the ViewGroup?

EDIT: A similar question on Google dev groups http://code.google.com/p/android/issues/detail?id=15526

回答1:

The Gallery widget has a known bug that causes it to always return a null convertView. This means that every time getView is called you are creating a new ImageView which might be causing the issue you're seeing.

There is a 3rd party created Gallery that Fixes the recycling bug, search around online for EcoGallery and you can find how to implement it. It will noticeably increase the graphics performance while you are scrolling the gallery.



回答2:

If you don't get it to work properly, I once made a hack that might help.

At the end of your doInBackground(), just before you return, do:

int viewsOnScreen = 3;
while(viewsOnScreen >= 1){ //adapt the numbers so that they work with your gallery, this is for a gallery with one visible child.
    viewsOnScreen = getLastVisiblePosition() - getFirstVisiblePosition();
    SystemClock.sleep(100);
}

This will wait with updating your image until you only display one view (or whatever number you specify, the point is you don't have any partial views on screen), causing the 'jump' to go by unnoticed. If your problems also involve jumps to a completely different position in the gallery, I recommend that you check which object you have selected before loading, and after loading you find what position this object is at, and call setSelection() to that position.

But try all the other solutions first...



回答3:

If you still haven't found a solution here is mine.

You need CustomGallery, which is almost 1:1 default Android Gallery. But you can't extend Gallery, you have to copy whole source code to your class. You will need to do the same with CustomAbsSpinner and CustomAdapterView classes. All this to change only one line of code... In your CustomGallery class change onLayout() method to this:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);

    /*
     * Remember that we are in layout to prevent more layout request from
     * being generated.
     */

    mInLayout = true;

    /*
     * Add if condition to avoid calling layout()  unnecessarily:
     * -when children calls onLayout after loading imageview
     */
    if(changed || mDataChanged)
        layout(0, false);
    mInLayout = false;
}

This will cause that layout() is called only when actually needed.

There was also another known bug in gallery behaviour, which causes gallery jumps on notifyDataSetChanged(). If you are having this bug as well just comment out one line in CustomAdapterView class:

    @Override
    public void onChanged() {
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();

        // Detect the case where a cursor that was previously invalidated has
        // been repopulated with new data.
        if (CustomAdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                && mOldItemCount == 0 && mItemCount > 0) {
            CustomAdapterView.this.onRestoreInstanceState(mInstanceState);
            mInstanceState = null;
        } else {
            rememberSyncState();
        }
        checkFocus();
        //comment this line to avoid gallery snap on notyfiDataSetChanged 
        //requestLayout();
    }

Anyway, good topic Marcos! Thank you for leading me to the solution. If you need source of the custom classes let me know.

EDIT (by Marcos Vasconcelos):

There's a bunch of styleables things that need to be copy in order to get the R fix for those classes.

For those who are searching the Android 2.2 source code (without res folder, if anyone found it please notify us), this can be found here: http://en.newinstance.it/2010/12/01/android-sdk-2-2_r2-sources/

For more recent versions of the API this can be done trough SDK Manager and will be located into /sources/android-



回答4:

Does it by any chance work if you (a) scroll really slowly, or (b) don't recycle the convertView?

I'll go out on a limb and guess that it does...

The reason being that when you recycle an ImageView from a previous call to getItem(), you are not cancelling the associated AsyncTask. You will have multiple tasks updating the same ImageView, which could be the behaviour you describe.



回答5:

you can use the concept of LazyList which fetches image from internet and temporary put in local cache which indirectly leads to smooth navigation in Gallery as image saved locally

here is the link https://github.com/thest1/LazyList or http://androidsnips.blogspot.com/2010/08/lazy-loading-of-images-in-list-view-in.html



回答6:

You can set a default bitmap before starting download of the actual image. This will make sure that the empty views dont get set with the wrong bitmaps.

// load defaultBmp in constructor.

image.setBackgroundColor(Color.WHITE);

image.setLayoutParams(new Gallery.LayoutParams(96, 170));

image.setImageBitmap(defaultBmp); // set the default bitmap (could be white). 
// execute AsyncTask here.