Android:动画在 KitKat 和 LolliPop 中有效,但在其他 API 版本中无效

Android: Animation works in KitKat and LolliPop but not on other API versions

我必须像 This 一样构建动画。 对不起,我没有太多的声誉来上传图片。你可以从上面的 link 中找到 gif 文件。 我已经完成了所有这些,并且它仅在 KitKat 和 LollyPop 上运行良好,但在其他 API 版本上运行良好。我正在使用 this 库。任何人都可以找出实际问题吗 这是我的代码。提前致谢

public abstract class AbstractSlideExpandableListAdapter extends WrapperListAdapterImpl

{

private View lastOpnUpperView = null;
ArrayList<View> upperViewsList = new ArrayList<View>();
ArrayList<View> lowerViewsList = new ArrayList<View>();
ArrayList<View> circleViewsList = new ArrayList<View>();


private View lastOpen = null;

private int lastOpenPosition = -1;

private int lastOpenItemIndex = 0;

private int animationDuration = 800;


private BitSet openItems = new BitSet();


private final SparseIntArray viewHeights = new SparseIntArray(10);


private ViewGroup parent;

public AbstractSlideExpandableListAdapter(ListAdapter wrapped)
{
    super(wrapped);
    lastOpenPosition = 0;
    openItems.set(lastOpenPosition, true);
}

private OnItemExpandCollapseListener expandCollapseListener;


public void setItemExpandCollapseListener(OnItemExpandCollapseListener listener)
{
    expandCollapseListener = listener;
}

public void removeItemExpandCollapseListener()
{
    expandCollapseListener = null;
}


public interface OnItemExpandCollapseListener
{

    public void onExpand(View itemView, int position);


    public void onCollapse(View itemView, int position);

}

private void notifiyExpandCollapseListener(int type, View view, int position)
{
    if (expandCollapseListener != null)
    {
        if (type == ExpandCollapseAnimation.EXPAND)
        {
            expandCollapseListener.onExpand(view, position);
        }
        else if (type == ExpandCollapseAnimation.COLLAPSE)
        {
            expandCollapseListener.onCollapse(view, position);
        }
    }

}

@Override
public View getView(int position, View view, ViewGroup viewGroup)
{
    this.parent = viewGroup;
    view = wrapped.getView(position, view, viewGroup);

    enableFor(view, position);

    return view;
}


public abstract View getExpandToggleButton(View parent);

public abstract View getExpandableView(View parent);

// upperView to expand animation for sequeeze
public abstract View getUpperView(View upperView);

// Lower view to expand and collapse
public abstract View getLowerView(View upperView);

// Get the circle view to hide and show
public abstract View getCircleView(View circleView);

/**
 * Gets the duration of the collapse animation in ms. Default is 330ms. Override this method to change the default.
 * 
 * @return the duration of the anim in ms
 */
public int getAnimationDuration()
{
    return animationDuration;
}

/**
 * Set's the Animation duration for the Expandable animation
 * 
 * @param duration
 *            The duration as an integer in MS (duration > 0)
 * @exception IllegalArgumentException
 *                if parameter is less than zero
 */
public void setAnimationDuration(int duration)
{
    if (duration < 0)
    {
        throw new IllegalArgumentException("Duration is less than zero");
    }

    animationDuration = duration;
}

/**
 * Check's if any position is currently Expanded To collapse the open item @see collapseLastOpen
 * 
 * @return boolean True if there is currently an item expanded, otherwise false
 */
public boolean isAnyItemExpanded()
{
    return (lastOpenPosition != -1) ? true : false;
}

public void enableFor(View parent, int position)
{
    View more = getExpandToggleButton(parent);
    View itemToolbar = getExpandableView(parent);
    View upperView = getUpperView(parent);
    View circleView = getCircleView(parent);
    View lowerView = getLowerView(parent);
    itemToolbar.measure(parent.getWidth(), parent.getHeight());

    upperViewsList.add(upperView);
    circleViewsList.add(circleView);
    lowerViewsList.add(lowerView);

    if (position == 0)
    {
        // lastopenUpperViewTemporary = upperView;
        lastOpnUpperView = upperView;
        upperView.setVisibility(View.GONE);
        lowerView.setVisibility(View.GONE);
    }
    enableFor(more, upperView, itemToolbar, position);
    itemToolbar.requestLayout();
}

private void animateListExpand(final View button, final View target, final int position)
{

    target.setAnimation(null);

    int type;
    if (target.getVisibility() == View.VISIBLE)
    {
        type = ExpandCollapseAnimation.COLLAPSE;
    }
    else
    {
        type = ExpandCollapseAnimation.EXPAND;
    }
    // remember the state
    if (type == ExpandCollapseAnimation.EXPAND)
    {
        openItems.set(position, true);
    }
    else
    {
        openItems.set(position, false);
    }

    // check if we need to collapse a different view
    if (type == ExpandCollapseAnimation.EXPAND)
    {
        if (lastOpenPosition != -1 && lastOpenPosition != position)
        {
            if (lastOpen != null)
            {
                animateWithUpperView(lastOpen, ExpandCollapseAnimation.COLLAPSE, position);
                // animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
                notifiyExpandCollapseListener(ExpandCollapseAnimation.COLLAPSE, lastOpen, lastOpenPosition);
            }
            openItems.set(lastOpenPosition, false);
        }

        lastOpen = target;
        lastOpenPosition = position;
    }
    else if (lastOpenPosition == position)
    {
        lastOpenPosition = -1;
    }
    // animateView(target, type);
    // Expand the view which was collapse
    Animation anim = new ExpandCollapseAnimation(target, type);
    anim.setDuration(getAnimationDuration());
    target.startAnimation(anim);

    this.notifiyExpandCollapseListener(type, target, position);
    // }
}

private void enableFor(final View button, final View upperView, final View target, final int position)
{
    // lastopenUpperViewTemporary = upperView;
    if (target == lastOpen && position != lastOpenPosition)
    {
        // lastOpen is recycled, so its reference is false
        lastOpen = null;
    }
    if (position == lastOpenPosition)
    {
        // re reference to the last view
        // so when can animate it when collapsed
        // lastOpen = target;
        lastOpen = target;
        lastOpnUpperView = upperView;
    }
    int height = viewHeights.get(position, -1);
    if (height == -1)
    {
        viewHeights.put(position, target.getMeasuredHeight());
        updateExpandable(target, position);
    }
    else
    {
        updateExpandable(target, position);
    }

    button.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(final View view)
        {

            System.out.println("Position: " + position);
            if (lastOpenPosition == position)
            {
                return;
            }
            System.out.println("Upper View: " + upperView);

            Animation anim = new ExpandCollapseUpperViewAnimation(upperViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
            anim.setDuration(800);
            anim.setAnimationListener(new AnimationListener()
            {

                @Override
                public void onAnimationStart(Animation animation)
                {
                    circleViewsList.get(position).setVisibility(View.GONE);

                }

                @Override
                public void onAnimationRepeat(Animation animation)
                {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onAnimationEnd(Animation animation)
                {
                    // TODO Auto-generated method stub
                    // upperViewsList.get(position).setVisibility(View.VISIBLE);
                    animateListExpand(button, target, position);
                }
            });
            upperViewsList.get(position).startAnimation(anim);

            // Lower animation

            Animation lowerAnim = new ExpandCollapseUpperViewAnimation(lowerViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
            lowerAnim.setDuration(800);
            lowerViewsList.get(position).startAnimation(lowerAnim);
        }
    });
}

private void updateExpandable(View target, int position)
{

    final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) target.getLayoutParams();
    if (openItems.get(position))
    {
        target.setVisibility(View.VISIBLE);
        params.bottomMargin = 0;
    }
    else
    {
        target.setVisibility(View.GONE);
        params.bottomMargin = 0 - viewHeights.get(position);
    }
}

/**
 * Performs either COLLAPSE or EXPAND animation on the target view
 * 
 * @param target
 *            the view to animate
 * @param type
 *            the animation type, either ExpandCollapseAnimation.COLLAPSE or ExpandCollapseAnimation.EXPAND
 */
private void animateView(final View target, final int type)
{
    Animation anim = new ExpandCollapseAnimation(target, type);
    anim.setDuration(getAnimationDuration());
    anim.setAnimationListener(new AnimationListener()
    {

        @Override
        public void onAnimationStart(Animation animation)
        {
        }

        @Override
        public void onAnimationRepeat(Animation animation)
        {
        }

        @Override
        public void onAnimationEnd(Animation animation)
        {
            System.out.println("Animation End");
            if (type == ExpandCollapseAnimation.EXPAND)
            {
                if (parent instanceof ListView)
                {
                    ListView listView = (ListView) parent;
                    int movement = target.getBottom();

                    Rect r = new Rect();
                    boolean visible = target.getGlobalVisibleRect(r);
                    Rect r2 = new Rect();
                    listView.getGlobalVisibleRect(r2);

                    if (!visible)
                    {
                        listView.smoothScrollBy(movement, getAnimationDuration());
                    }
                    else
                    {
                        if (r2.bottom == r.bottom)
                        {
                            listView.smoothScrollBy(movement, getAnimationDuration());
                        }
                    }
                }
            }

        }
    });
    target.startAnimation(anim);
}

private void animateWithUpperView(final View target, final int type, final int position)
{
    Animation anim = new ExpandCollapseAnimation(target, type);
    anim.setDuration(getAnimationDuration());
    anim.setAnimationListener(new AnimationListener()
    {
        @Override
        public void onAnimationStart(Animation animation)
        {
        }

        @Override
        public void onAnimationRepeat(Animation animation)
        {
        }

        @Override
        public void onAnimationEnd(Animation animation)
        {
            // upperViewsList.get(lastOpenItemForUpperView).setVisibility(View.VISIBLE);
            // lastOpenItemForUpperView = position;
            Animation expandItemAniamtion = new ExpandCollapseLowerViewAnimation(upperViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
            expandItemAniamtion.setDuration(800);
            expandItemAniamtion.setAnimationListener(new AnimationListener()
            {

                @Override
                public void onAnimationStart(Animation animation)
                {

                }

                @Override
                public void onAnimationRepeat(Animation animation)
                {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onAnimationEnd(Animation animation)
                {
                    circleViewsList.get(lastOpenItemIndex).setVisibility(View.VISIBLE);
                    lastOpenItemIndex = position;
                }
            });

            // Lower view animation
            Animation lowerAnim = new ExpandCollapseLowerViewAnimation(lowerViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
            lowerAnim.setDuration(800);

            upperViewsList.get(lastOpenItemIndex).startAnimation(expandItemAniamtion);

            lowerViewsList.get(lastOpenItemIndex).startAnimation(lowerAnim);
        }
    });
    target.startAnimation(anim);
}

/**
 * Closes the current open item. If it is current visible it will be closed with an animation.
 * 
 * @return true if an item was closed, false otherwise
 */
public boolean collapseLastOpen()
{
    if (isAnyItemExpanded())
    {
        // if visible animate it out
        if (lastOpen != null)
        {
            animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
        }
        openItems.set(lastOpenPosition, false);
        lastOpenPosition = -1;
        return true;
    }
    return false;
}

public Parcelable onSaveInstanceState(Parcelable parcelable)
{

    SavedState ss = new SavedState(parcelable);
    ss.lastOpenPosition = this.lastOpenPosition;
    ss.openItems = this.openItems;
    return ss;
}

public void onRestoreInstanceState(SavedState state)
{

    if (state != null)
    {
        this.lastOpenPosition = state.lastOpenPosition;
        this.openItems = state.openItems;
    }
}

/**
 * Utility methods to read and write a bitset from and to a Parcel
 */
private static BitSet readBitSet(Parcel src)
{
    BitSet set = new BitSet();
    if (src == null)
    {
        return set;
    }
    int cardinality = src.readInt();

    for (int i = 0; i < cardinality; i++)
    {
        set.set(src.readInt());
    }

    return set;
}

private static void writeBitSet(Parcel dest, BitSet set)
{
    int nextSetBit = -1;

    if (dest == null || set == null)
    {
        return; // at least dont crash
    }

    dest.writeInt(set.cardinality());

    while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
    {
        dest.writeInt(nextSetBit);
    }
}

/**
 * The actual state class
 */
static class SavedState extends View.BaseSavedState
{
    public BitSet openItems = null;
    public int lastOpenPosition = -1;

    SavedState(Parcelable superState)
    {
        super(superState);
    }

    private SavedState(Parcel in)
    {
        super(in);
        lastOpenPosition = in.readInt();
        openItems = readBitSet(in);
    }

    @Override
    public void writeToParcel(Parcel out, int flags)
    {
        super.writeToParcel(out, flags);
        out.writeInt(lastOpenPosition);
        writeBitSet(out, openItems);
    }

    // required field that makes Parcelables from a Parcel
    public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
    {
        public SavedState createFromParcel(Parcel in)
        {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size)
        {
            return new SavedState[size];
        }
    };
}

public static Animation ExpandOrCollapseView(final View v, final boolean expand)
{
    try
    {
        Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
        m.setAccessible(true);
        m.invoke(v, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(((View) v.getParent()).getMeasuredWidth(), MeasureSpec.AT_MOST));
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    final int initialHeight = v.getMeasuredHeight();

    if (expand)
    {
        v.getLayoutParams().height = 0;
    }
    else
    {
        v.getLayoutParams().height = initialHeight;
    }
    v.setVisibility(View.VISIBLE);

    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t)
        {
            int newHeight = 0;
            if (expand)
            {
                newHeight = (int) (initialHeight * interpolatedTime);
            }
            else
            {
                newHeight = (int) (initialHeight * (1 - interpolatedTime));
            }
            v.getLayoutParams().height = newHeight;
            v.requestLayout();

            if (interpolatedTime == 1 && !expand)
                v.setVisibility(View.GONE);
        }

        @Override
        public boolean willChangeBounds()
        {
            return true;
        }
    };
    a.setDuration(1000);
    return a;
}}

幸运的是我找到了解决方案。在 pre-kitkat 上,消失的列表项上的 upperView 不会强制设置动画,但在 kitkat 上,它会强制设置动画,从消失变为可见。 因此,作为解决方案,我们必须在列表项和上层视图之间提供一个视图层(我们必须对其进行动画处理)。 这样它就会像魅力一样工作。