美文网首页Android开发经验谈Android技术知识Android开发
阅读翻页动画之我想用RecyclerView+LayoutMan

阅读翻页动画之我想用RecyclerView+LayoutMan

作者: 橘子周二 | 来源:发表于2020-04-04 12:06 被阅读0次

    基于ReccyclerView 自定义LayoutManger实现的阅读器动画

    1.已发布版本

        implementation 'com.github.HarkBen:ReadAnim:0.2'
    

    2.进度记录

    • 主要目的:解耦ItemView独立的事件处理能力,自定义布局能力。

    • 未解决问题:

      • 实现仿真动画时,放弃Canvas2D动画(部分手机表现很差,不利于处理事件,不利于页面自定义解耦),改用opengl 3D动画,因为是基于bitmap来更新动画,暂时没有好想法处理实时获取ItemView的bitmap。
      • 下层itemView的事件获取

    2.1 目前效果

    • 垂直翻页 :完全解耦
    image
    • 覆盖滑动 :完全解耦
    image
    • 横向滚动 :完全解耦
    image
    • 仿真卷曲动画 :未解耦
    image

    普通动画实现思路:自定义LayoutManger,处理滑动及itemview显示位置,根据滑动方向将下一个或上一个View提前展示,利用scale 控制屏幕的view显示状态,利用Rotation 实现翻页效果,利用viewZ轴层级准确提供页面层级,利用阴影增加上下页的立体视觉。

    核心代码:

         if (bookFilpMode == MODE_HORIZATIONAL_SLIDE || bookFilpMode == MODE_FILP) {
                    //view 复用时 需要重制属性
                    if (itemIndex + 1 <= fastVisiPos) {
                        //计算下一个view
                        View nextView = recycler.getViewForPosition(itemIndex + 1);
                        nextView.setScaleX(1f);
                        nextView.setScaleY(1f);
                        nextView.setTranslationX(0);
                        nextView.setRotationY(0f);
                        if (itemIndex <= focusPosition) {
                            addView(nextView);
                        } else {
                            addView(nextView, 0);
                        }
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            nextView.setElevation(-1);
                        }
                        measureChildWithMargins(nextView, 0, 0);
                        int nl, nr;
                        nl = 0;
                        nr = nl + getDecoratedMeasurementHorizontal(nextView);
                        layoutDecoratedWithMargins(nextView, nl, 0, nr, bottom);
                    }
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    itemView.setElevation(0);
                }
                layoutDecoratedWithMargins(itemView, left, top, right, bottom);
    
                if (bookFilpMode == MODE_HORIZATIONAL_SLIDE) {
                    itemView.setTranslationX(dx);
                    final int childCenterX = (right + left) / 2;
                    final int parentCenterX = getWidth() / 2;
                    boolean isChildLayoutLeft = childCenterX <= parentCenterX;
                    if (isChildLayoutLeft) {
                        itemView.setScaleX(1f);
                        itemView.setScaleY(1f);
                    } else {
                        itemView.setTranslationX(0);
                        itemView.setScaleX(0);
                        itemView.setScaleY(0);
                    }
                } else if (bookFilpMode == MODE_FILP) {
                    final int childCenterX = (right + left) / 2;
                    final int parentCenterX = getWidth() / 2;
                    boolean isChildLayoutLeft = childCenterX <= parentCenterX;
                    if (!isChildLayoutLeft) {//隐藏屏幕外的
                        itemView.setTranslationX(0);
                        itemView.setScaleX(0);
                        itemView.setScaleY(0);
                    }else {
                        itemView.setScaleX(1F);
                        itemView.setScaleY(1F);
                    }
                    float offset = (horizontalOffset * 1.0f) % (childWidth * 1.0f);
                    float rataitonY = Math.abs(offset / childWidth * 90);
                    itemView.setTranslationX(offset);//一边沿着Y轴旋转一边调整位置
                    itemView.setCameraDistance(-50000);//无限拉近相机焦点
                    itemView.setPivotX(0);
                    itemView.setRotationY(-rataitonY);//沿着Y 反转
                }else {
                    itemView.setTranslationX(dx);
                    itemView.setScaleX(1f);
                    itemView.setScaleY(1f);
                    itemView.setRotationY(0f);
                }
    

    仿真卷曲动画实现思路:使用开源库pagecurl的卷曲动画,在Layoutmanger中提前准备好Itemview的缓存bitmap,或者直接使用RecyclerView的缓存bitmap,并提供给pagecurl。

    核心代码:

            @Override
            public int getPageCount() {
                return adapter.getItemCount();
            }
    
            @Override
            public void updatePage(CurlPage page, int width, int height, int index) {
                int lastIndex = curlLayoutManger.getSelectIndex();
                RBLog.log("updatePage index=%s lastIndex=%s", index, lastIndex);
                Message message = handler.obtainMessage();
                message.arg1 = index;
                handler.sendMessage(message);
                recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
                recyclerView.setDrawingCacheEnabled(true);
                recyclerView.buildDrawingCache();
                Bitmap bitmap = recyclerView.getDrawingCache().copy(Bitmap.Config.RGB_565, false);
                recyclerView.setDrawingCacheEnabled(false);
                recyclerView.destroyDrawingCache();
    //                Bitmap front = curlLayoutManger.getItemBitmap().copy(Bitmap.Config.RGB_565,false);
                page.setTexture(bitmap, CurlPage.SIDE_BOTH);
                page.setColor(Color.rgb(0xFF, 0xFF, 0xFF), CurlPage.SIDE_BACK);
            }
        }
    

    3.期待希望阅读此篇的你能提供建议~,我都会进行尝试

    源码地址:https://github.com/HarkBen/ReadAnim

    4.感谢以下(不限于)文章:

    Android自定义控件进阶篇,自定义LayoutManager

    Android自定义LayoutManager第十一式之飞龙在天

    张旭童的掌握自定义LayoutManager(一) 系列开篇 常见误区、问题、注意事项,常用API

    PageCurl


    原文地址:https://juejin.im/post/5e87f986518825736512cb70

    END

    相关文章

      网友评论

        本文标题:阅读翻页动画之我想用RecyclerView+LayoutMan

        本文链接:https://www.haomeiwen.com/subject/augdphtx.html