对所有可见的recyclerview进行动画处理

时间:2018-10-16 07:00:46

标签: android android-recyclerview recycler-adapter

我已经看过this之类的问题,但我想在特定程度上翻译recyclerview的项目

我尝试过this库,并且需要动画。

我要在动画下面。

enter image description here

所以请建议我应该如何实现道具动画!

这是我到目前为止尝试过的东西

  1. recyclerview的设置:

    final RecyclerView recyclerView = findViewById(R.id.recycler);
    final CircleLayoutManager circleLayoutManager = new CircleLayoutManager(this);
    final ScrollZoomLayoutManager scrollZoomLayoutManager = new ScrollZoomLayoutManager(this, Dp2px(10));
    
    SpacesItemDecoration spacesItemDecoration = new SpacesItemDecoration(
            (int) getResources().getDimension(R.dimen._minus30sdp));
    recyclerView.addItemDecoration(spacesItemDecoration);
    recyclerView.setItemViewCacheSize(0);
    
    adapter = new Adapter();
    recyclerView.addOnScrollListener(new CenterScrollListener());
    recyclerView.setLayoutManager(circleLayoutManager);
    recyclerView.setItemViewCacheSize(0);
    recyclerView.setAdapter(adapter);
    
  2. 布局管理器的设置:

    public class CircleLayoutManager extends RecyclerView.LayoutManager {
    
    private static int INTERVAL_ANGLE = 90;// The default interval angle between each items
    private static float DISTANCE_RATIO = 10f; // Finger swipe distance divide item rotate angle
    
    //Flags of scroll dirction
    private static int SCROLL_LEFT = 1;
    private static int SCROLL_RIGHT = 2;
    
    private Context context;
    
    // Size of each items
    private int mDecoratedChildWidth;
    private int mDecoratedChildHeight;
    
    //Property
    private int startLeft;
    private int startTop;
    private int mRadius;
    private int intervalAngle;
    private float offsetRotate; // The offset angle for each items which will change according to the scroll offset
    
    //the range of remove from parent
    private int minRemoveDegree;
    private int maxRemoveDegree;
    
    //initial position of content
    private int contentOffsetX = -1;
    private int contentOffsetY = -1;
    
    private int firstChildRotate = 0;
    
    //Sparse array for recording the attachment and rotate angle of each items
    private SparseBooleanArray itemAttached = new SparseBooleanArray();
    private SparseArray<Float> itemsRotate = new SparseArray<>();
    
    
    public CircleLayoutManager(Context context) {
        this.context = context;
        intervalAngle = INTERVAL_ANGLE;
        offsetRotate = 0;
        minRemoveDegree = -100;
        maxRemoveDegree = 100;
    }
    
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getItemCount() == 0) {
            detachAndScrapAttachedViews(recycler);
            offsetRotate = 0;
            return;
        }
    
        //calculate the size of child
        if (getChildCount() == 0) {
            View scrap = recycler.getViewForPosition(0);
            addView(scrap);
            measureChildWithMargins(scrap, 0, 0);
            mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
            mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
            startLeft = contentOffsetX == -1 ? (getHorizontalSpace() - mDecoratedChildWidth) / 2 : contentOffsetX;
            startTop = contentOffsetY == -1 ? 0 : contentOffsetY;
            mRadius = mDecoratedChildHeight;
            detachAndScrapView(scrap, recycler);
        }
    
        //record the state of each items
        float rotate = firstChildRotate;
        for (int i = 0; i < getItemCount(); i++) {
            itemsRotate.put(i, rotate);
            itemAttached.put(i, false);
            rotate += intervalAngle;
        }
    
        detachAndScrapAttachedViews(recycler);
        fixRotateOffset();
        layoutItems(recycler, state);
    }
    
    private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
        layoutItems(recycler, state, SCROLL_RIGHT);
    }
    
    private void layoutItems(RecyclerView.Recycler recycler,
                             RecyclerView.State state, int oritention) {
        if (state.isPreLayout()) return;
    
        //remove the views which out of range
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            int position = getPosition(view);
            if (itemsRotate.get(position) - offsetRotate > maxRemoveDegree
                    || itemsRotate.get(position) - offsetRotate < minRemoveDegree) {
                itemAttached.put(position, false);
                removeAndRecycleView(view, recycler);
            }
        }
    
        //add the views which do not attached and in the range
        for (int i = 0; i < getItemCount(); i++) {
            if (itemsRotate.get(i) - offsetRotate <= maxRemoveDegree
                    && itemsRotate.get(i) - offsetRotate >= minRemoveDegree) {
                if (!itemAttached.get(i)) {
                    View scrap = recycler.getViewForPosition(i);
                    measureChildWithMargins(scrap, 0, 0);
                    if (oritention == SCROLL_LEFT)
                        addView(scrap, 0);
                    else
                        addView(scrap);
                    float rotate = itemsRotate.get(i) - offsetRotate;
                    int left = calLeftPosition(rotate);
                    int top = calTopPosition(rotate);
                    scrap.setRotation(rotate);
                    layoutDecorated(scrap, startLeft + left, startTop + top,
                            startLeft + left + mDecoratedChildWidth, startTop + top + mDecoratedChildHeight);
                    itemAttached.put(i, true);
                }
            }
        }
    }
    
    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int willScroll = dx;
    
        float theta = dx / DISTANCE_RATIO; // the angle every item will rotate for each dx
        float targetRotate = offsetRotate + theta;
    
        //handle the boundary
        if (targetRotate < 0) {
            willScroll = (int) (-offsetRotate * DISTANCE_RATIO);
        } else if (targetRotate > getMaxOffsetDegree()) {
            willScroll = (int) ((getMaxOffsetDegree() - offsetRotate) * DISTANCE_RATIO);
        }
        theta = willScroll / DISTANCE_RATIO;
    
        offsetRotate += theta; //increase the offset rotate so when re-layout it can recycle the right views
    
        //re-calculate the rotate x,y of each items
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            float newRotate = view.getRotation() - theta;
            int offsetX = calLeftPosition(newRotate);
            int offsetY = calTopPosition(newRotate);
            layoutDecorated(view, startLeft + offsetX, startTop + offsetY,
                    startLeft + offsetX + mDecoratedChildWidth, startTop + offsetY + mDecoratedChildHeight);
            view.setRotation(newRotate);
        }
    
        //different direction child will overlap different way
        if (dx < 0)
            layoutItems(recycler, state, SCROLL_LEFT);
        else
            layoutItems(recycler, state, SCROLL_RIGHT);
        return willScroll;
    }
    
    /**
     * @param rotate the current rotate of view
     * @return the x of view
     */
    private int calLeftPosition(float rotate) {
        return (int) (mRadius * Math.cos(Math.toRadians(90 - rotate)));
    }
    
    /**
     * @param rotate the current rotate of view
     * @return the y of view
     */
    private int calTopPosition(float rotate) {
        return (int) (mRadius - mRadius * Math.sin(Math.toRadians(90 - rotate)));
    }
    
    private int getHorizontalSpace() {
        return getWidth() - getPaddingRight() - getPaddingLeft();
    }
    
    private int getVerticalSpace() {
        return getHeight() - getPaddingBottom() - getPaddingTop();
    }
    
    /**
     * fix the offset rotate angle in case item out of boundary
     **/
    private void fixRotateOffset() {
        if (offsetRotate < 0) {
            offsetRotate = 0;
        }
        if (offsetRotate > getMaxOffsetDegree()) {
            offsetRotate = getMaxOffsetDegree();
        }
    }
    
    /**
     * @return the max degrees according to current number of views and interval angle
     */
    private float getMaxOffsetDegree() {
        return (getItemCount() - 1) * intervalAngle;
    }
    
    private PointF computeScrollVectorForPosition(int targetPosition) {
        if (getChildCount() == 0) {
            return null;
        }
        final int firstChildPos = getPosition(getChildAt(0));
        final int direction = targetPosition < firstChildPos ? -1 : 1;
        return new PointF(direction, 0);
    }
    
    @Override
    public boolean canScrollHorizontally() {
        return true;
    }
    
    @Override
    public void scrollToPosition(int position) {
        if (position < 0 || position > getItemCount() - 1) return;
        float targetRotate = position * intervalAngle;
        if (targetRotate == offsetRotate) return;
        offsetRotate = targetRotate;
        fixRotateOffset();
        requestLayout();
    }
    
    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        LinearSmoothScroller smoothScroller = new LinearSmoothScroller(context) {
            @Override
            public PointF computeScrollVectorForPosition(int targetPosition) {
                return CircleLayoutManager.this.computeScrollVectorForPosition(targetPosition);
            }
    
           /* @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                return X / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, Y, displayMetrics);
            }*/
    
        };
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }
    
    @Override
    public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
        removeAllViews();
        offsetRotate = 0;
    }
    
    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    
    /**
     * @return Get the current positon of views
     */
    public int getCurrentPosition() {
        return Math.round(offsetRotate / intervalAngle);
    }
    
    /**
     * @return Get the dx should be scrolled to the center
     */
    public int getOffsetCenterView() {
        return (int) ((getCurrentPosition() * intervalAngle - offsetRotate) * DISTANCE_RATIO);
    }
    
    /**
     * @return Get the radius of the circle
     */
    public int getRadius() {
        return mRadius;
    }
    
    /**
     * @param mRadius the radius of the circle,default will be item's height
     */
    public void setRadius(int mRadius) {
        this.mRadius = mRadius;
    }
    
    /**
     * @return the interval angle between each items
     */
    public int getIntervalAngle() {
        return intervalAngle;
    }
    
    /**
     * Default angle is 30
     *
     * @param intervalAngle the interval angle between each items
     */
    public void setIntervalAngle(int intervalAngle) {
        this.intervalAngle = intervalAngle;
    }
    
    /**
     * Default is center in parent
     *
     * @return the content offset of x
     */
    public int getContentOffsetX() {
        return contentOffsetX;
    }
    
    /**
     * Default is center in parent
     *
     * @param contentOffsetX the content offset of x
     */
    public void setContentOffsetX(int contentOffsetX) {
        this.contentOffsetX = contentOffsetX;
    }
    
    /**
     * Default is top in parent
     *
     * @return the content offset of y
     */
    public int getContentOffsetY() {
        return contentOffsetY;
    }
    
    /**
     * Default is top in parent
     *
     * @param contentOffsetY the content offset of y
     */
    public void setContentOffsetY(int contentOffsetY) {
        this.contentOffsetY = contentOffsetY;
    }
    
    /**
     * Default is 0
     *
     * @return the rotate of first child
     */
    public int getFirstChildRotate() {
        return firstChildRotate;
    }
    
    /**
     * Default is 0
     *
     * @param firstChildRotate the rotate of first child
     */
    public void setFirstChildRotate(int firstChildRotate) {
        this.firstChildRotate = firstChildRotate;
    }
    
    /**
     * The rotate of child view in range[min,max] will be shown,default will be [-90,90]
     *
     * @param min min rotate that will be show
     * @param max max rotate that will be show
     */
    public void setDegreeRangeWillShow(int min, int max) {
        if (min > max) return;
        minRemoveDegree = min;
        maxRemoveDegree = max;
    }
    }
    
  3. 这是我到现在为止的结果

enter image description here

谢谢。

0 个答案:

没有答案