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(视图组件)如何在多个页面不同视觉规范下的复用

  • 相关阅读:
    Tips(持续跟新)
    icpc 2018 徐州 网络赛 B 博弈+记忆化搜索
    2018 徐州 icpc 网络赛 A 递推or数学公式
    2018 徐州icpc网络赛 G 分块
    HDU 3092 Least common multiple(完全背包+思维)
    hdu 4747(DP?线性递推)
    Pell-方程学习小结
    C++中map的介绍用法以及Gym题目:Two Sequences
    求最长上升子序列和最长非下降子序列
    dfs+枚举,flip游戏的拓展POJ2965
  • 原文地址:https://www.cnblogs.com/163yun/p/9099666.html
Copyright © 2011-2022 走看看