Android LayoutAnimation does not take effect

test environment

Pixel + Android 10 + Android Studio 3.5.3

Problem Description

Added LayoutAnimation to GridView , but no animation is performed after startup

  • 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="match_parent">

    <GridView android:id="@+id/gv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:horizontalSpacing="@dimen/spacing"
        android:verticalSpacing="@dimen/spacing"
        android:listSelector="@color/transparent"
        android:layoutAnimation="@anim/layout_anim"
        android:numColumns="auto_fit"/>
    <Button android:id="@+id/add_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/app_name"/>
</RelativeLayout>
  • layout_anim.xml
<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:rowDelay="50%"
    android:columnDelay="40%"
    android:directionPriority="none"
    android:direction="top_to_bottom|left_to_right"
    android:animation="@anim/anim"/>
  • anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="600">
    <translate android:fromYDelta="50%p" android:toYDelta="0"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" />
</set>

Cause Analysis

First look at the code in the java part:

public class Test extends Activity {
    
    
    private GridAdapter mGrideAdapter;
    private GridView grid;
    private List<String> mDatas = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        grid = (GridView) findViewById(R.id.gv);
        mDatas.addAll(getData());
        mGrideAdapter = new GridAdapter();
        grid.setAdapter(mGrideAdapter);

        Button addData = (Button)findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                addData();
                mGrideAdapter.notifyDataSetChanged();
            }
        });
    }


    private List<String> getData() {
    
    

        List<String> data = new ArrayList<String>();
        for (int i = 1;i<15;i++){
    
    
            data.add("DATA "+i);
        }
        return data;
    }


    public void addData(){
    
    
        mDatas.addAll(mDatas);
        //mGrideAdapter.notifyDataSetChanged();
    }

    public class GridAdapter extends BaseAdapter {
    
    
        public View getView(int position, View view, ViewGroup parent) {
    
    
            Item item;
            if(view != null && view.getTag() != null){
    
    
                item = (Item)view.getTag();
            }else{
    
    
                item = new Item();
                view = getLayoutInflater().inflate(R.layout.item_cover, null, false);
                item.cover = (ImageView)view.findViewById(R.id.ivCover);
                item.title = (TextView)view.findViewById(R.id.title);
            }
            item.title.setText(mDatas.get(position));
            return view;
        }

        class Item {
    
    
            ImageView cover;
            TextView title;
        }

        public final int getCount() {
    
    
            return mDatas.size();
        }

        public final Object getItem(int position) {
    
    
            return null;
        }

        public final long getItemId(int position) {
    
    
            return position;
        }
    }
}

The above test code, in the process of independent execution, there is no problem.
However, there will be problems when porting to another project, and the root of the problem is:
After the GridView is initialized, the LayoutAnimation is not executed or during the execution process, If you call BaseAdapter.notifyDataSetChanged, LayoutAnimation will be aborted immediately

In the offending code, in the Adapter, an asynchronous loading image is added, and the UI is updated through notifyDataSetChanged, and when notifyDataSetChanged is called, it is determined by the speed of asynchronous loading of the image, usually, it will be executed earlier than LayoutAnimation.

In general, LayoutAnimation is only executed once

  • frameworks/base/core/java/android/view/ViewGroup.java
    // When set, dispatchDraw() will run the layout animation and unset the flag
	private static final int FLAG_RUN_ANIMATION = 0x8;

    @Override
    protected void dispatchDraw(Canvas canvas) {
    
    
        //...
        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
    
    
            final boolean buildCache = !isHardwareAccelerated();
            for (int i = 0; i < childrenCount; i++) {
    
    
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
    
    
                    final LayoutParams params = child.getLayoutParams();
                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                    bindLayoutAnimation(child);
                }
            }

            final LayoutAnimationController controller = mLayoutAnimationController;
            if (controller.willOverlap()) {
    
    
                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
            }

            controller.start();
			//清除标识, 后续不再执行LayoutAnimation
            mGroupFlags &= ~FLAG_RUN_ANIMATION;
            mGroupFlags &= ~FLAG_ANIMATION_DONE;

            if (mAnimationListener != null) {
    
    
                mAnimationListener.onAnimationStart(controller.getAnimation());
            }
        }
        //...
    }
    /**
     * Runs the layout animation. Calling this method triggers a relayout of
     * this view group.
     */
    public void startLayoutAnimation() {
    
    
        if (mLayoutAnimationController != null) {
    
    
            mGroupFlags |= FLAG_RUN_ANIMATION;
            requestLayout();
        }
    }

    /**
     * Schedules the layout animation to be played after the next layout pass
     * of this view group. This can be used to restart the layout animation
     * when the content of the view group changes or when the activity is
     * paused and resumed.
     */
    public void scheduleLayoutAnimation() {
    
    
        mGroupFlags |= FLAG_RUN_ANIMATION;
    }

If you need to re-execute, you can try ViewGroup.scheduleLayoutAnimation()

reference

Animation of the custom control trilogy (11)-layoutAnimation and gridLayoutAnimation

Guess you like

Origin blog.csdn.net/ansondroider/article/details/104318397