项目添加无效的列表视图动画

时间:2015-08-17 15:28:23

标签: android performance listview android-listview

我正在尝试修改this示例(code),以便在将项目添加到listview时,listview会在添加新项目时同时滚动。默认行为是首先为新项目创建空间,然后添加新项目。

我得到了大部分正确但问题是listview不会滚动到最后添加的项目。它只是在当前显示的视图中动画。

这是完整代码的github repo

这是listview和适配器的代码

    public class InsertionListView extends ListView {

    private static final int NEW_ROW_DURATION = 500;
    private static final int OVERSHOOT_INTERPOLATOR_TENSION = 5;

    private OvershootInterpolator sOvershootInterpolator;

    private RelativeLayout mLayout;

    private Context mContext;

    private List<ListItemObject> mData;
    private List<BitmapDrawable> mCellBitmapDrawables;

    public InsertionListView(Context context) {
        super(context);
        init(context);
    }

    public InsertionListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public InsertionListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public void init(Context context) {
        setDivider(null);
        mContext = context;
        mCellBitmapDrawables = new ArrayList<BitmapDrawable>();
        sOvershootInterpolator = new OvershootInterpolator(OVERSHOOT_INTERPOLATOR_TENSION);
    }

    /**
     * Modifies the underlying data set and adapter through the addition of the new object
     * to the first item of the ListView. The new cell is then animated into place from
     * above the bounds of the ListView.
     */
    public void addRow(ListItemObject newObj) {

        final CustomArrayAdapter adapter = (CustomArrayAdapter)getAdapter();
        /**
         * Stores the starting bounds and the corresponding bitmap drawables of every
         * cell present in the ListView before the data set change takes place.
         */
        final HashMap<Long, Rect> listViewItemBounds = new HashMap<Long, Rect>();
        final HashMap<Long, BitmapDrawable> listViewItemDrawables = new HashMap<Long,
                BitmapDrawable>();

        int firstVisiblePosition = getFirstVisiblePosition();
        for (int i = 0; i < getChildCount(); ++i) {

            View child = getChildAt(i);
            int position = firstVisiblePosition + i;
            long itemID = adapter.getItemId(position);
            Rect startRect = new Rect(child.getLeft(), child.getTop(), child.getRight(),
                    child.getBottom());
            listViewItemBounds.put(itemID, startRect);
            listViewItemDrawables.put(itemID, getBitmapDrawableFromView(child));
        }

        /** Adds the new object to the data set, thereby modifying the adapter,
         *  as well as adding a stable Id for that specified object.*/
        mData.add(newObj);
        adapter.addStableIdForDataAtPosition(mData.size()-1);
        adapter.notifyDataSetChanged();
        final ViewTreeObserver observer = getViewTreeObserver();

        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {

                observer.removeOnPreDrawListener(this);
                ArrayList<Animator> animations = new ArrayList<Animator>();

                final View newCell = getChildAt(0);
                final ImageView imgView = (ImageView)newCell.findViewById(R.id.image_view);
                final ImageView copyImgView = new ImageView(mContext);
                int firstVisiblePosition = getFirstVisiblePosition();


                /** Loops through all the current visible cells in the ListView and animates
                 * all of them into their post layout positions from their original positions.*/
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    int position = firstVisiblePosition + i;
                    long itemId = adapter.getItemId(position);
                    Rect startRect = listViewItemBounds.get(itemId);
                    int top = child.getTop();
                    if (startRect != null) {
                        /** If the cell was visible before the data set change and
                         * after the data set change, then animate the cell between
                         * the two positions.*/
                        int startTop = startRect.top;
                        int delta = startTop - top;
                        ObjectAnimator animation = ObjectAnimator.ofFloat(child,
                                View.TRANSLATION_Y, 150, 0);
                        animations.add(animation);
                    } else {
                        /** If the cell was not visible (or present) before the data set
                         * change but is visible after the data set change, then use its
                         * height to determine the delta by which it should be animated.*/
                        int childHeight = child.getHeight() + getDividerHeight();
                        int startTop = top + (i > 0 ? childHeight : -childHeight);
                        int delta = startTop - top;
                        ObjectAnimator animation = ObjectAnimator.ofFloat(child,
                                View.TRANSLATION_Y, 150, 0);
                        animations.add(animation);
                    }
                    listViewItemBounds.remove(itemId);
                    listViewItemDrawables.remove(itemId);
                }

                /**
                 * Loops through all the cells that were visible before the data set
                 * changed but not after, and keeps track of their corresponding
                 * drawables. The bounds of each drawable are then animated from the
                 * original state to the new one (off the screen). By storing all
                 * the drawables that meet this criteria, they can be redrawn on top
                 * of the ListView via dispatchDraw as they are animating.
                 */
                for (Long itemId: listViewItemBounds.keySet()) {
                    BitmapDrawable bitmapDrawable = listViewItemDrawables.get(itemId);
                    Rect startBounds = listViewItemBounds.get(itemId);
                    bitmapDrawable.setBounds(startBounds);

                    int childHeight = startBounds.bottom - startBounds.top + getDividerHeight();
                    Rect endBounds = new Rect(startBounds);
                    endBounds.offset(0, childHeight);

                    ObjectAnimator animation = ObjectAnimator.ofObject(bitmapDrawable,
                            "bounds", sBoundsEvaluator, startBounds, endBounds);
                    animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        private Rect mLastBound = null;
                        private Rect mCurrentBound = new Rect();
                        @Override
                        public void onAnimationUpdate(ValueAnimator valueAnimator) {
                            Rect bounds = (Rect)valueAnimator.getAnimatedValue();
                            mCurrentBound.set(bounds);
                            if (mLastBound != null) {
                                mCurrentBound.union(mLastBound);
                            }
                            mLastBound = bounds;
                            invalidate(mCurrentBound);
                        }
                    });

                    listViewItemBounds.remove(itemId);
                    listViewItemDrawables.remove(itemId);

                    mCellBitmapDrawables.add(bitmapDrawable);

                    animations.add(animation);
                }

                /** Animates all the cells from their old position to their new position
                 *  at the same time.*/
                setEnabled(false);
                AnimatorSet set = new AnimatorSet();
                set.setDuration(NEW_ROW_DURATION);
                set.playTogether(animations);
                set.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        mCellBitmapDrawables.clear();
                        imgView.setVisibility(View.VISIBLE);
                        mLayout.removeView(copyImgView);
                        setEnabled(true);
                        invalidate();
                    }
                });
                set.start();

                listViewItemBounds.clear();
                listViewItemDrawables.clear();
                return true;
            }
        });
    }

    /**
     * By overriding dispatchDraw, the BitmapDrawables of all the cells that were on the
     * screen before (but not after) the layout are drawn and animated off the screen.
     */
    @Override
    protected void dispatchDraw (Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mCellBitmapDrawables.size() > 0) {
            for (BitmapDrawable bitmapDrawable: mCellBitmapDrawables) {
                bitmapDrawable.draw(canvas);
            }
        }
    }


    /** Returns a bitmap drawable showing a screenshot of the view passed in. */
    private BitmapDrawable getBitmapDrawableFromView(View v) {
        Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas (bitmap);
        v.draw(canvas);
        return new BitmapDrawable(getResources(), bitmap);
    }

    /** Setter for the underlying data set controlling the adapter. */
    public void setData(List<ListItemObject> data) {
        mData = data;
    }

    /**
     * Setter for the parent RelativeLayout of this ListView. A reference to this
     * ViewGroup is required in order to add the custom animated overlaying bitmap
     * when adding a new row.
     */
    public void setLayout(RelativeLayout layout) {
        mLayout = layout;
    }

    /**
     * This TypeEvaluator is used to animate the position of a BitmapDrawable
     * by updating its bounds.
     */
    static final TypeEvaluator<Rect> sBoundsEvaluator = new TypeEvaluator<Rect>() {
        public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
            return new Rect(interpolate(startValue.left, endValue.left, fraction),
                    interpolate(startValue.top, endValue.top, fraction),
                    interpolate(startValue.right, endValue.right, fraction),
                    interpolate(startValue.bottom, endValue.bottom, fraction));
        }

        public int interpolate(int start, int end, float fraction) {
            return (int)(start + fraction * (end - start));
        }
    };

} 

适配器代码

    public class CustomArrayAdapter extends ArrayAdapter<ListItemObject> {

    HashMap<ListItemObject, Integer> mIdMap = new HashMap<ListItemObject, Integer>();
    List<ListItemObject> mData;
    Context mContext;
    int mLayoutViewResourceId;
    int mCounter;

    public CustomArrayAdapter(Context context, int layoutViewResourceId,
                              List <ListItemObject> data) {
        super(context, layoutViewResourceId, data);
        mData = data;
        mContext = context;
        mLayoutViewResourceId = layoutViewResourceId;
        updateStableIds();
    }

    public long getItemId(int position) {
        ListItemObject item = getItem(position);
        if (mIdMap.containsKey(item)) {
            return mIdMap.get(item);
        }
        return -1;
    }

    public void updateStableIds() {
        mIdMap.clear();
        mCounter = 0;
        for (int i = 0; i < mData.size(); ++i) {
            mIdMap.put(mData.get(i), mCounter++);
        }
    }

    public void addStableIdForDataAtPosition(int position) {
        mIdMap.put(mData.get(position), ++mCounter);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ListItemObject obj = mData.get(position);

        if(convertView == null) {
            LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
            convertView = inflater.inflate(mLayoutViewResourceId, parent, false);
        }

        convertView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams
                .MATCH_PARENT, obj.getHeight()));

        ImageView imgView = (ImageView)convertView.findViewById(R.id.image_view);
        TextView textView = (TextView)convertView.findViewById(R.id.text_view);

        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
                obj.getImgResource(), null);

        textView.setText(obj.getTitle());
        imgView.setImageBitmap(CustomArrayAdapter.getCroppedBitmap(bitmap));

        return convertView;
    }

    /**
     * Returns a circular cropped version of the bitmap passed in.
     */
    public static Bitmap getCroppedBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
                Config.ARGB_8888);

        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

        Canvas canvas = new Canvas(output);

        final Paint paint = new Paint();
        paint.setAntiAlias(true);

        int halfWidth = bitmap.getWidth() / 2;
        int halfHeight = bitmap.getHeight() / 2;

        canvas.drawCircle(halfWidth, halfHeight, Math.max(halfWidth, halfHeight), paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));

        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }
} 

活动

    public class InsertingCells extends Activity implements OnRowAdditionAnimationListener {

    private ListItemObject mValues[];

    private InsertionListView mListView;

    private Button mButton;

    private Integer mItemNum = 0;

    private RoundView mRoundView;

    private int mCellHeight;

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

        mValues = new ListItemObject[] {
                new ListItemObject("Chameleon", R.drawable.chameleon, 0),
                new ListItemObject("Rock", R.drawable.rock, 0),
                new ListItemObject("Flower", R.drawable.flower, 0),
        };

        mCellHeight = (int)(getResources().getDimension(R.dimen.cell_height));

        List<ListItemObject> mData = new ArrayList<ListItemObject>();
        CustomArrayAdapter mAdapter = new CustomArrayAdapter(this, R.layout.list_view_item, mData);
        RelativeLayout mLayout = (RelativeLayout)findViewById(R.id.relative_layout);

        mRoundView = (RoundView)findViewById(R.id.round_view);
        mButton = (Button)findViewById(R.id.add_row_button);
        mListView = (InsertionListView)findViewById(R.id.listview);
        mListView.setAdapter(mAdapter);
        mListView.setData(mData);
        mListView.setLayout(mLayout);
    }

    public void addRow(View view) {
        mItemNum++;
        ListItemObject obj = mValues[mItemNum % mValues.length];
        final ListItemObject newObj = new ListItemObject(obj.getTitle(), obj.getImgResource(),
                mCellHeight);
        mListView.addRow(newObj);
        ObjectAnimator animator = mRoundView.getScalingAnimator();
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationRepeat(Animator animation) {
                mListView.addRow(newObj);
            }
        });
        animator.start();
    }

    @Override
    public void onRowAdditionAnimationStart() {
        mButton.setEnabled(false);
    }

    @Override
    public void onRowAdditionAnimationEnd() {
        mButton.setEnabled(true);
    }
}

Listview行

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/item_linear_layout"
          android:layout_height="match_parent"
          android:layout_width="match_parent"
          android:orientation="horizontal"
          android:background="@drawable/border">

<ImageView
        android:id="@+id/image_view"
        android:layout_height="match_parent"
        android:layout_width="0dp"
        android:gravity="center_vertical"
        android:layout_weight="1"
        android:scaleType="center"/>

<TextView
        android:id="@+id/text_view"
        android:layout_height="fill_parent"
        android:layout_width="0dp"
        android:gravity="center"
        android:layout_weight="2"
        android:textStyle="bold"
        android:textSize="22sp"
        android:textColor="#ffffff"/>

0 个答案:

没有答案