zoukankan      html  css  js  c++  java
  • Box(视图组件)如何在多个页面不同视觉规范下的复用

    本文来自 网易云社区

    问题描述

    Android App中的页面元素,都是由一个个Box(可以理解成一个个自定义View组件和Widget同级)组成,这些Box可以在不同的页面、不同的模块达到复用的效果。但是,现在遇到了一个对于开发复用棘手的问题,

    • A页面的组件间距和B页面的组件间距可能不同。

    • A页面的Box1与Box1间距,和Box1与Box2的间距不一样。

    • Box和Box之间的分割线,有粗有细,有的有左边距。

    等等还有许多需要动态调整的地方。

    然后做这些Box组件,就是为了复用它们,但现在又要对于每个Box的外边距,如Padding值,进行修改,以适应不同页面的要求。

    方案

    方案一

    当然首先想到的是每个Box就和View一样,都有自己的属性,可以更改它的边距等属性,每个Box都有自己的视图模型ViewModel,可以在ViewModel中添加配置项,如:

    public class ViewModel {
        int leftPadding;
        int topPadding;
        int rightPadding;
        int bottomPadding;
    
        public void setPadding(int left, int top, int right, int bottom
        {...}    
    }
    
    //在Box的update方法,根据配置的ViewModel,更新视图
    public void update() {
        if(viewModel != null) {
            setPadding(viewModel.leftPadding, viewModel.topPadding,
            viewModel.rightPadding, viewModel.bottomPadding);
        }
    }
    

    这种办法,确实可行,但是会增加了开发的工作量,增加了代码的繁琐程度。毕竟Box的ViewModel应该尽量简单,ViewModel的存在应该只是让内部可以配置,而且主要配置的是Box要显示的数据。

    如果每个要复用的Box都要增加这些属性,然后适配ViewModel的时候还要考虑每一个Box的四周边距,有时候还不能统一对待。比如一个List存放着同一种类型的Box,但是恰好最后一个Box它需要底边距不一样,那岂不还要适配ViewModel的时候还要做序数判断?

    可想而知,开发代价是多高。

    方案二

    下面讲个实践中觉得最好的方案: 每个页面其实就是负责组装这些Box,因此负责组装的页面应该知道Box与Box之间的边距,分割线等外置属性。因此这些外置边距就交给RecyclerView.ItemDecoration类。

    首先我们需要知道RecyclerView.ItemDecoration类能干什么?

     

    假设绿色区域代表的是我们的内容,红色区域代表我们自己绘制的装饰,可以看到:

    图1:代表了getItemOffsets(),可以实现类似padding的效果。

    图2:代表了onDraw(),可以实现类似绘制背景的效果,内容在上面。

    图3:代表了onDrawOver(),可以绘制在内容的上面,覆盖内容。

    不过在交给它之前,还需要明确Box的边界。

    • 蓝色框内为可复用的Box
    • 红色外框是RecycleView中的一个ChildView大小

    然后就有了如下方案:

    约定:要复用的Box内部不应该有距离上下左右的边距,要保证最小边界。

    Box与Box之间的边距交给RecyclerView.ItemDecorationgetItemOffset()方法处理。

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        DividerDesc dividerDesc = getDividerDesc(view, parent);
        outRect.set(dividerDesc.leftPadding, dividerDesc.topPadding, dividerDesc.rightPadding, dividerDesc.bottomPadding);
    }
    

    我们可以通过getDividerDesc()方法,通过当前View及RecycleView里的Adapter里的ItemType。得知当前View需要在四周有怎么样的属性及分割线。

    DividerDesc类可以是这样:

    private static class DividerDesc {
    
            final boolean needLeftMargin;    //水平分割线左边距
            final int leftPadding;            //左边距
            final int topPadding;            //上边距
            final int rightPadding;            //右边距
            final int bottomPadding;        //底边距
            final int dividerHeight;        //底部分割线高度
    
            DividerDesc(boolean needLeftMargin, int leftPadding, int topPadding, int rightPadding, int bottomPadding, int dividerHeight) {
                this.needLeftMargin = needLeftMargin;
                this.leftPadding = leftPadding;
                this.topPadding = topPadding;
                this.rightPadding = rightPadding;
                this.bottomPadding = bottomPadding;
                this.dividerHeight = dividerHeight;
            }
    
            DividerDesc() {
                this(false, 0, 0, 0, 0, 0);
            }
        }
    

    RecyclerView.ItemDecorationdrawOver()方法中,画出需要分割线的地方。

    @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            int left,right,top,bottom;
    
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount - 1; i++) {
                left = parent.getPaddingLeft();
                right = parent.getWidth() - parent.getPaddingRight();
    
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
    
                DividerDesc dividerDesc = getDividerDesc(child, parent);
                //带左边距
                if (dividerDesc.needLeftMargin) {
                    left += sThinMarginHorizontal;
                }
    
                bottom = child.getBottom() + dividerDesc.bottomPadding;
                top = bottom - dividerDesc.dividerHeight;
    
                if (dividerDesc.dividerHeight == sThinHeight) {
                    //画细线
                    mDividerThin.setBounds(left, top, right, bottom);
                    mDividerThin.draw(c);
                } else if (dividerDesc.dividerHeight == sThickHeight){
                    //画粗线
                    mDividerThick.setBounds(left, top, right, bottom);
                    mDividerThick.draw(c);
                }
    
            }
    

    Demo效果如下:

    未加边距:

    增加边距:

    如果你也遇到了为Box复用而没法处理繁琐的视觉调整问题,或者有更好的方案,欢迎一块讨论 :)

    参考文章

    本文已由作者陈柏宁授权网易云社区发布,原文链接:Box(视图组件)如何在多个页面不同视觉规范下的复用

  • 相关阅读:
    奇数阶魔方问题
    《DSP using MATLAB》示例9.3
    《DSP using MATLAB》示例9.2
    《DSP using MATLAB》示例9.1
    找个目标很重要
    《DSP using MATLAB》示例Example 8.30
    《DSP using MATLAB》示例Example 8.29
    《DSP using MATLAB》示例Example 8.28
    《DSP using MATLAB》示例Example 8.27
    《DSP using MATLAB》示例Example 8.26
  • 原文地址:https://www.cnblogs.com/163yun/p/9099666.html
Copyright © 2011-2022 走看看