精华内容
下载资源
问答
  • 大家好,我是蜗牛君~ 我们又见面了,本篇文章是蜗牛君漫聊动态布局框架的第二篇。上一篇中我们讲解了框架的大致思路,以及复习了一下RecyclerView的基础使用方式。那么本篇文章我们就正式开始框架的搭建了。 首先...

    Hello! 大家好,我是蜗牛君~ 我们又见面了,本篇文章是蜗牛君漫聊动态布局框架的第二篇。上一篇中我们讲解了框架的大致思路,以及复习了一下RecyclerView的基础使用方式。那么本篇文章我们就正式开始框架的搭建了。

    首先我们要做一件事情,就是捋清楚整个框架的搭建思路。

    思路讲解

    1、思路的起点:getItemViewType(int position)方法

    只要是有Android原生开发经验的朋友都知道在RecyclerView出现之前,我们一直熟练使用的是ListView,为了解决ListView的一些不理想之处,所以出现了RecyclerView,但是ListView的大部分功能RecyclerView还是继承了。蜗牛君在很久之前使用ListView做开发时就遇到过一个需求,在新闻列表中要穿插广告,广告的布局样式和新闻布局完全不同,这个需求很常见,所以蜗牛君在网上很轻易的就查到了大量的文章,这些文章的内容大同小异,他们都有一个共同点,那就是使用Adapter的getItemViewType(int position)方法。

    于是蜗牛君就在网上查了一下这个方法的用途。大概意思就是说在Adapter创建ViewHolder之前,会先通过getItemViewType方法判断数据的类型,可以根据getItemViewType方法不同的返回值,确定加载不同的ViewHolder。因此,getItemViewType方法就是思路的起点。

    那我们在getItemViewType方法中做什么呢?由于getItemViewType方法的返回值是int类型,因此这个int类型的值要能够确定ViewHolder的具体类型。

    2、数据类型和ViewHolder类型对应关系的存储

    我们框架的原理就是根据不同类型的数据选择不同类型的ViewHolder,因此这份一一对应的关系如何存储至关重要。

    我们的思路是使用HashMap集合,数据类型的Class类为Key值,ViewHolder类的Class类为Value值,这样就把一一对应的关系存储起来了。

    既然我们存储的是Class类,因此在实例化的时候就需要用到Java的反射机制了,这也就是动态的体现。

    3、数据和ViewHolder对应关系的查询

    在HashMap集合中,Key值是数据类型的Class类,我们接下来要思考的问题是如何确定Key值。上文提到过,getItemViewType方法返回的数据类型是int类型,而我们HashMap中存储的Key值是Class类型,这完全不匹配啊!我们的策略是中间增加一层,建立Key值的索引目录。

    具体来讲就是,我们在初始化时,要进行一步存储操作,这步操作包含两项:第一项就是将数据类型的Class类依次存入ArrayList集合;第二项是依次将数据与ViewHolder的对应关系存入HashMap;有了存储所有数据类型Class类的ArrayList集合,我们就可以确定当前Item的数据类型是否存在,存在的话又是什么。确定了数据的Class类,就能从HashMap中获取到对应ViewHolder的Class类。

    4、程序的设计

    对于ViewHolder的创建,我们使用工厂模式,不同类型的ViewHolder是我们要生产的产品,我们还需要一个生产ViewHolder的工厂,对于工厂模式的基本结构这里不再赘述。

    对外提供使用方法的时候,使用建造者模式,以此达到链式调用的目的。

    代码实现:框架的核心功能—工厂模式

    1、工厂模式中的抽象产品类:BaseViewHolder

    需要做到以下几点:

    • 继承官方RecyclerView.ViewHolder类;
    • 以泛型的方式传递要绑定的数据类型;
    • 重写构造方法;
    • 重载构造方法;
    • 填充Item布局文件(以参数形式);
    • 绑定数据到Item布局(以参数形式);
    /**
     * 工厂模式中---抽象产品类
     *
     * 职责:
     * 1、填充Item布局文件;
     * 2、绑定数据到Item布局
     *
     * @param <V> 需要绑定的数据类型
     */
    public abstract class BaseViewHolder<V> extends RecyclerView.ViewHolder {
        /**
         * 自定义构造方法
         * 
         * 无论是ListView还是RecyclerView,我们都是使用LayoutInflater的方式填充Item的布局
         * 因此这里要传递的参数是Context、ViewGroup、LayoutId
         * 
         * @param context
         * @param parent
         * @param layoutId
         */
        public BaseViewHolder(Context context, ViewGroup parent, int layoutId) {
            this(LayoutInflater.from(context).inflate(layoutId, parent, false));
        }
    
        public BaseViewHolder(@NonNull View itemView) {
            super(itemView);
            // 使用ButterKnife绑定布局文件
            ButterKnife.bind(this,itemView);
        }
    
        /**
         * ViewHolder与数据进行绑定
         *
         * @param position
         * @param value
         * @param adapter
         */
        public abstract void bindTo(int position, V value, RecyclerAdapter adapter);
    }
    

    注意

    • 我们使用了ButterKnife进行布局关联;
    • 抽象方法bindTo中的参数定义,value是必须的,这是我们进行绑定的具体数据,其他参数可根据业务需要进行修改,具体实现由子类完成;

    2、工厂模式中的具体产品类—CommonViewHolder

    需要做到一下几点:

    • 传递具体ViewHolder所要绑定的数据类型给父类;
    • 重载父类的构造方法,确定了父类构造方法中三个参数里面的layoutId的值;
    • 进行真正的数据与布局的绑定工作;
    /**
     * ViewHolder具体实现类
     */
    public class CommonViewHolder extends BaseViewHolder<CommonModel> {
        @BindView(R.id.txt_content)
        TextView mTxtContent;
    
        /**
         * 重载构造方法,确定LayoutId参数值
         * 
         * @param context
         * @param parent
         */
        public CommonViewHolder(Context context, ViewGroup parent) {
            super(LayoutInflater.from(context).inflate(R.layout.recycler_view_item_common, parent, false));
        }
        
        @Override
        public void bindTo(int position, CommonModel value, RecyclerAdapter adapter) {
            mTxtContent.setText(value.content);
        }
    }
    

    recycler_view_item_common.xml(使用ConstraintLayout布局)

    <TextView
        android:id="@+id/txt_content"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.097"
        tools:ignore="MissingConstraints" />
    

    3、工厂模式中的具体工厂类—ViewHolderFactory

    因为我们模块只需要一个工厂类,因此不必设置抽象工厂类。

    需要做到以下几点:

    • 数据类型与ViewHolder对应关系的存储策略:
      创建List类型的索引集合,用来存储所有数据的具体类型;创建Map类型的ViewHolder集合,Key值为数据的具体类型,Value值为ViewHolder的具体类型。
    • 动态创建ViewHolder的核心技术:Java的反射机制。
    • 封装对外提供的基本方法。
    /**
     * ViewHolder工厂类
     */
    public class ViewHolderFactory {
        private Context mContext;
        // 数据类型的Class类作为索引被存储在集合中
        private List<Class> valueClassType = new ArrayList<>();
        // 数据类型的Class类为Key值,需要绑定的ViewHolder类型的Class类为Value值
        private Map<Class, Class<? extends BaseViewHolder>> boundViewHolder = new HashMap<>();
    
        /**
         * 构造方法,需要传递上下文
         * 
         * @param context
         */
        public ViewHolderFactory(Context context) {
            mContext = context;
        }
    
        /**
         * 创建ViewHolder
         *
         * @param viewType Map中的Key值所在List集合中的下标
         * @param parent
         * @return
         */
        public BaseViewHolder create(int viewType, ViewGroup parent) {
            Class valueClass = valueClassType.get(viewType);
            try {
                Class<? extends BaseViewHolder> viewHolderClass = boundViewHolder.get(valueClass);
                Constructor<? extends BaseViewHolder> constructor = viewHolderClass.getDeclaredConstructor(Context.class, ViewGroup.class);
                return constructor.newInstance(mContext, parent);
            } catch (Exception e) {
                throw new RuntimeException("Unable to create ViewHolder for " + valueClass + "."
                        + e.getCause().getMessage(), e);
            }
        }
    
        /**
         * 存储ViewHolder索引,存储ViewHolder
         *
         * @param valueClass
         * @param viewHolder
         */
        public void bind(Class valueClass, Class<? extends BaseViewHolder> viewHolder) {
            // 存储索引值
            valueClassType.add(valueClass);
            // 存储ViewHolder
            boundViewHolder.put(valueClass, viewHolder);
        }
    
        /**
         * 查询指定数据类型在索引集合中的下标,有下标就能在Map中找到对应的ViewHolder类型
         *
         * @param object
         * @return
         */
        public int itemViewType(Object object) {
            return valueClassType.indexOf(object.getClass());
        }
    
        /**
         * 获取索引集合
         *
         * @return
         */
        public List<Class> getValueClassType() {
            return valueClassType;
        }
    
        /**
         * 获取ViewHolder集合
         *
         * @return
         */
        public Map<Class, Class<? extends BaseViewHolder>> getBoundViewHolder() {
            return boundViewHolder;
        }
    }
    

    以上就是框架核心功能工厂模式的全部代码了,由于篇幅原因,本篇文章先介绍到这里。上述内容有一定的难度,首先第一步要做到的就是缕清思路,理解制定的方案与意图。第二步就是一些基础知识的获取,工厂模式的相关知识,Java反射机制的相关知识等等。好了,蜗牛君想说的就这么多,我们下篇文章见。

    在这里插入图片描述

    展开全文
  • 导语大家好,我是智能仓储物流技术研习社的社长,你的老朋友,老K。知识星球*原创电子书*深海社区*微信群工厂企业在开展精益生产时,不可避免的就需要开展工厂规划设计,对原来的生产...

    导语

    大家好,我是智能仓储物流技术研习社的社长,你的老朋友,老K。

    知识星球 * 原创电子书 * 深海社区 * 微信群 

    工厂企业在开展精益生产时,不可避免的就需要开展工厂规划设计,对原来的生产现场进行改善。工厂规划布局简单地说布局就是对设备、工作台、物料、工装、半成品、水、电、气等的综合配置。

    工厂布局规划主要是研究工序之间、车间之间以及 工厂整体的设备、工作台、原材料、成品仓库等配置的合理性,以达到整个生产系统的人流与物流畅通化、搬运最优化、流程最优化、效率最大化的目标。

    工厂规划设计与改善的六大原则:

    1、统一原则,在布局设计与改善时,必须将各工序的人、机、料、法4要素有机结合起来并保持充分的平衡。因为,四要素一旦没有统一协调好,作业容易割裂,会延长停滞时间,增加物料搬运的次数。

    2、最短距离原则,在布局设计与改善时,必须要遵循移动距离、移动时间最小化。因为移动距离越短,物料搬运所花费的费用和时间就越小。

    3、人流、物流畅通原则,在进行工厂布局设计与改善时,必须使工序没有堵塞,物流畅通无阻。在工厂布局设计时应注意:尽量避免倒流和交叉现象,否则会导致一系列意想不到的后果,如品质问题、管理难度问题、生产效率问题、安全问题等。

    4、充分利用立体空间原则,随着地价的不断攀升,企业厂房投资成本也水涨船高,因此,如何充分利用立体空间就变得尤其重要,它直接影响到产品直接成本的高低。

    5、安全满意原则,在进行工厂布局设计与改善时,必须确保作业人员的作业既安全又轻松,因为只有这样才能减轻作业疲劳度。请切记:材料的移动、旋转动作等可能会产生安全事故,抬升、卸下货物动作等也可能会产生安全事故。

    6、灵活机动原则,在进行工厂布局设计与改善时,应尽可能做到适应变化、随机应变,如面对工序的增减、产能的增减能灵活对应。为了能达成灵活机动原则,在设计时需要将水、电、气与作业台分离、不要连成一体,设备尽量不要落地生根而采用方便移动的装置。

    我们在对生产车间进行产线布局设计时,需要遵从一些车间流水线布局的原则,而这些原则可以概括为:“两个遵守、两个回避”。两个遵守:逆时针排布、出入口一致;两个回避:孤岛型布局、鸟笼型布局。

    一、两个遵守

    1:逆时针排布

    逆时针排布,主要目的是希望员工能够采用一人完结作业方式,能够实现一人多机。一人完结与一人多机要求一个员工从头做到尾,因此员工是动态的,称之为“巡回作业”。

    大部分作业员是右撇子,因此如果逆时针排布的话,当员工进行下一道加工作业时,工装夹具或者零部件在左侧,员工作业并不方便,这也正是逆时针的目的,员工就会走到下一工位,巡回的目的也就达到了。

    2:出入口一致转

    出入口一致,是指原材料入口和成品出口在一起。首先,有利于减少空手浪费。假设出入口不一致,作业员采用巡回作业,那么当一件产品生产完了,要去重新取一件原材料加工的话,作业员就会空手(手上没有材料可以生产)从成品产出口走到原材料投入口,这段时间是浪费。如果出入口一致的话,作业员立刻就可以取到新的原材料进行加工,从而避免了空手浪费。

    其次,有利于生产线平衡。由于出入口一致,布局必然呈现类似“U”的形状,这使得各工序非常接近,从而为一个人同时操作多道工序提供了可能,这就提高了工序分配的灵活性,从而取得更高的生产线平衡率。

    二、两个回避

    1:孤岛型布局

    孤岛型布局把生产线分割成一个个单独的工作单元,其缺陷在于单元与单元之间互相隔离,无法互相协助;

    2:鸟笼型布局

    鸟笼型布局往往没有考虑到物流、人流顺畅的结果,这种布局错误的用机器设备或者工作台把作业员围在中间,使得物流不顺畅,在制品增加,单元与单元之间的相互支援也变得几乎不可能。

    三、三种布局

    1、瓣型布局

    花瓣型布局是由多个单元共同组成,是按照“两个遵守、两个回避”原则进行布局的结果。这种布局有助于提高单元间的互相协助,从而提高生产线平衡率。花瓣式布局是进行“互助协作”的必要条件。

    互助作业就好像运动场上的接力赛一样,在交接区,实力强的选手可以适当的弥补实力弱的选手。在工作中也是一样,单元之间的作业员也要把产品像接力棒一样去传递。如果前单元的作业员耽误了时间,后单元的作业员就帮助前单元从机器上卸下产品。这种做法,显然依靠“花瓣式”布局为前提。

    2、“一个流”生产线布局

    按精益生产的理念,除了以上的两个方面,还有更重要的因素需要考虑,精益生产布局追求的目标主要有以下几方面:使物料搬运成本最小化;空间的有效利用;劳动力的有效利用;便于员工之间、员工与管理者之间、员工与用户之间的信息沟通。一个流(One—Piece Flow)”生产方式是指产品在生产过程中尽量实现小批量的轮转,最好是单件的流动。

    它是减少在制品数量的最好方法,是消除生产线浪费的良策。它的基础是要使加工生产线像流水装配线一样流水化,但要牺牲非瓶颈工序设备的利用率,因此要尽量平衡生产线,减少设备的产能损失。

    3、“U”型布置

    车间流水线布置一般归纳为如下两种:传统“一”字型生产线布置(即按加工顺序排列设备)和u型布置。随着精益生产思想的推广,传统生产线越来越多地被U型生产线所代替,因为传统生产线布置有如下缺点:一个人操作多台设备时将存在“步行的浪费”,增加了劳动强度,同时也不能实现人员的柔性化调整。

    而在U型布置中,生产线摆放如U型,一条流水线的出口和人口在相同位置,一个加工位置中可能同时包含几个工艺,所以U型布置需要培养多能工。它减少了步行浪费和工位数,从而缩短周期、提高效率,同时也减少了操作工,降低了成本等。



    知名企业


     


     

    -智能仓储物流技术研习社-

    建立智能物流系统甲方、集成商与周边配套商

    共同技术语言,填埋沟通鸿沟
    提供高校物流专业教学现实素材

    搭建可实际应用、接地气的

    智能物流技术交流分享平台

    立体库 | AGV | 机器人 | 拣选 | 分拣 | 仿真 

    | WMS | WCS | 输送 | 包装

    |规划| |报告| |趋势| |历史| 

    制造业| 电商 | 快递 | 鞋服 | 家具 | 商超 

    | 零售 | 医药 | 冷链 | 第三方

    往期推荐

    老K汇总|中国托盘四向车|厂商目录

    AGV|简单粗暴分类及其各自优劣势

    上汽智能仓储|28堆垛机|200AGV|20机械手|……

    26吨铝箔卷立体库、RGV| 超重载日常出入库

    2020中国AI+仓储物流发展研究报告

    致2021年所有想辞职的物流技术从业者

    物流自动化系统面临的5项风险点!

    WMS系统各业务环节规则及功能设计

    1.7吨长条型材立体库!特制重载堆垛机

    中国物流自动化装备行业2020概览

    优秀的物流自动化售前规划师应具备哪些条件?

    展开全文
  • 其实在前文(一)已经提到了,把页面、模块、布局都抽象成组件类,就如同Jsp标签一样 这种标签类负责输出每个模块的内容,当然包含了业务数据在里面,具体参考一下几个类   大家会发现:在每个组件类中都强制...

    (2)加载用户定制页面的第二种方案

    其实在前文(一)已经提到了,把页面、模块、布局都抽象成组件类,就如同Jsp标签一样

    这种标签类负责输出每个模块的内容,当然包含了业务数据在里面,具体参考一下几个类

     

    大家会发现:在每个组件类中都强制实现toString方法,其实就是使用FreeMarker的功能,每个组件类都有属性对应ftl模板,并且又有数据属性

    所以使用FreeMarker的process方法,传入ftl模板和数据既可以把布局、模板都输出来

    这才是为什么设计布局、模块表结构时,为什么要记录其对应的ftl模板路径

    当然:要注意一点,因为所有ftl模板是已经定制好的,放在某个固定的目录,可以在系统启动之后先一次加载完所有模板

    那么每个组件类只要传入(模板所在路径+数据)即可得到页面

    (相信使用过FreeMarker来模拟Hibernate或者MyBatis来做动态SQL的同学都明白我的意思)

     

    所有组件的父类:BaseTag

    /**
     * 
     */
    package com.yli.cshop.bean;
    
    /**
     * 
     * 把页面/布局/模块都抽象成一个个组件,就好比Jsp标签一样<br>
     * BaseTag 则是所有组件的父类,强制子类重写toString方法
     * 
     * @author yli
     *
     */
    public abstract class BaseTag {
    
    	/**
    	 * 重写toString()方法
    	 */
    	public abstract String toString();
    
    }
    

     

     

    负责输出页面的组件类:Page

    package com.yli.cshop.bean;
    
    import java.util.List;
    
    /**
     * 页面可以认为是所有组件的父容器<br>
     * 它可以包含有固定部分和动态布局的部分<br>
     * 
     * @author yli
     * 
     */
    public class Page extends BaseTag {
    
    	/**
    	 * 页面ID-系统生成的ID
    	 */
    	private long pageId;
    
    	/**
    	 * 页面名称-用户可自由添加页面,定义页面名称
    	 */
    	private String pageName;
    
    	/**
    	 * 页面序号-用户可以自由拖动页面排序,记录该序号
    	 */
    	private int sortNo;
    
    	/**
    	 * 当前页面包含的布局列表<br>
    	 * 布局又包含了模块,所以组成了完整的页面
    	 */
    	private List<Layout> layoutList;
    
    	public long getPageId() {
    		return pageId;
    	}
    
    	public void setPageId(long pageId) {
    		this.pageId = pageId;
    	}
    
    	public String getPageName() {
    		return pageName;
    	}
    
    	public void setPageName(String pageName) {
    		this.pageName = pageName;
    	}
    
    	public List<Layout> getLayoutList() {
    		return layoutList;
    	}
    
    	public void setLayoutList(List<Layout> layoutList) {
    		this.layoutList = layoutList;
    	}
    
    	public int getSortNo() {
    		return sortNo;
    	}
    
    	public void setSortNo(int sortNo) {
    		this.sortNo = sortNo;
    	}
    
    	public String generateHtml() {
    
    		return null;
    	}
    
    	@Override
    	public String toString() {
    		if (null != layoutList) {
    			StringBuffer buffer = new StringBuffer();
    			for (Layout layout : layoutList) {
    				buffer.append(layout.toString());
    			}
    			return buffer.toString();
    		}
    		return null;
    	}
    
    }
    

     

    负责输出布局的组件类:Layout

    package com.yli.cshop.bean;
    
    import java.util.List;
    
    import com.yli.cshop.util.FreeMakerParser;
    
    /**
     * 布局可以认为是模块的父容器<br>
     * 用户可将模块自由添加进来,并对模块排序<br>
     * 布局其实也可以看做模块,只是它不负责具体的业务内容而已<br>
     * 
     * @author yli
     *
     */
    public class Layout extends BaseTag {
    
    	/**
    	 * 布局ID-系统生成的ID
    	 */
    	private long layoutId;
    
    	/**
    	 * 所属页面ID-建立页面与布局的关系
    	 */
    	private long pageId;
    
    	/**
    	 * 布局开始html标签-对应一个FreeMarker模板地址
    	 */
    	private String beginTemplate;
    
    	/**
    	 * 布局结束html标签-对应一个FreeMarker模板地址
    	 * 
    	 */
    	private String endTemplate;
    	
    	/**
    	 * 布局序号-用户可在页面添加多个布局,并自由排序,记录该序号
    	 */
    	private int sortNo;
    
    	/**
    	 * 当前布局包含的模块列表
    	 */
    	private List<BaseModule> moduleList;
    
    	public long getLayoutId() {
    		return layoutId;
    	}
    
    	public void setLayoutId(long layoutId) {
    		this.layoutId = layoutId;
    	}
    
    	public long getPageId() {
    		return pageId;
    	}
    
    	public void setPageId(long pageId) {
    		this.pageId = pageId;
    	}
    
    	public String getBeginTemplate() {
    		return beginTemplate;
    	}
    
    	public void setBeginTemplate(String beginTemplate) {
    		this.beginTemplate = beginTemplate;
    	}
    
    	public String getEndTemplate() {
    		return endTemplate;
    	}
    
    	public void setEndTemplate(String endTemplate) {
    		this.endTemplate = endTemplate;
    	}
    	
    	public int getSortNo() {
    		return sortNo;
    	}
    
    	public void setSortNo(int sortNo) {
    		this.sortNo = sortNo;
    	}
    
    	public List<BaseModule> getModuleList() {
    		return moduleList;
    	}
    
    	public void setModuleList(List<BaseModule> moduleList) {
    		this.moduleList = moduleList;
    	}
    
    	@Override
    	public String toString() {
    		StringBuffer buffer = new StringBuffer();
    		String beginHtml = FreeMakerParser.process(beginTemplate, null);
    		String endHtml = FreeMakerParser.process(endTemplate, null);
    		buffer.append(beginHtml);
    		for (BaseModule module : moduleList) {
    			buffer.append(module.toString());
    		}
    		buffer.append(endHtml);
    		return buffer.toString();
    	}
    }
    

     

    特别注意:负责输出模块的组件类:BaseModule

    package com.yli.cshop.bean;
    
    import com.yli.cshop.util.FreeMakerParser;
    
    /**
     * 虽然模块和布局一样,负责输出一段html标签<br>
     * 但是模块是负责各自业务模块的,比如有[公告信息模块][轮播广告模块]<br>
     * 意味着每个模块还会关联各自的业务内容,即关联到业务表<br>
     * 但是每个业务模块要[保存/更新/删除]的业务内容:所对应的的业务类、方法肯定不一样<br>
     * 所以把模块抽象出来的同时,把每个模块对应的业务类和业务内容定义成两个属性<br>
     * 
     * @author yli
     *
     */
    public class BaseModule extends BaseTag {
    
    	/**
    	 * 模板ID-系统生成的ID
    	 */
    	private long moduleId;
    
    	/**
    	 * 布局的ID-建立布局与模块的关系
    	 */
    	private long layoutId;
    
    	/**
    	 * 模块名称-用户可以自定义模块名称
    	 */
    	private String moduleName;
    
    	/**
    	 * 模块序号-用户可自由拖动模块,记录该模块在当前布局的序号
    	 */
    	private int sortNo;
    
    	/**
    	 * 模块对应的FreeMarker模板路径-因为每个模块"长的样子肯定不一样"
    	 */
    	private String moduleTemplate;
    
    	/**
    	 * 模块包含的业务数据-不同的业务模块对应的数据和数据类型不一样
    	 */
    	private Object moduleContent;
    	
    	/**
    	 * 模块对应的业务处理类-此处记录业务Bean名称即可
    	 */
    	private String moduleServiceBean;
    
    	public long getModuleId() {
    		return moduleId;
    	}
    
    	public void setModuleId(long moduleId) {
    		this.moduleId = moduleId;
    	}
    
    	public long getLayoutId() {
    		return layoutId;
    	}
    
    	public void setLayoutId(long layoutId) {
    		this.layoutId = layoutId;
    	}
    
    	public String getModuleName() {
    		return moduleName;
    	}
    
    	public void setModuleName(String moduleName) {
    		this.moduleName = moduleName;
    	}
    
    	public int getSortNo() {
    		return sortNo;
    	}
    
    	public void setSortNo(int sortNo) {
    		this.sortNo = sortNo;
    	}
    
    	public String getModuleTemplate() {
    		return moduleTemplate;
    	}
    
    	public void setModuleTemplate(String moduleTemplate) {
    		this.moduleTemplate = moduleTemplate;
    	}
    
    	public Object getModuleContent() {
    		return moduleContent;
    	}
    
    	public void setModuleContent(Object moduleContent) {
    		this.moduleContent = moduleContent;
    	}
    
    	public String getModuleServiceBean() {
    		return moduleServiceBean;
    	}
    
    	public void setModuleServiceBean(String moduleServiceBean) {
    		this.moduleServiceBean = moduleServiceBean;
    	}
    
    
    	@Override
    	public String toString() {
    		StringBuffer buffer = new StringBuffer();
    		String moduleInfo = FreeMakerParser.process(moduleTemplate,	moduleContent);
    		buffer.append(moduleInfo);
    		return buffer.toString();
    	}
    
    }
    

     

    所有组件类已经写好了,那么就准备布局、模块对应的FreeMarker模板文件即可,如下所示 

    我为页面、布局和每种模块都定制了一个ftl页面,放在classpath目录的conf目录下

     

     

     

    为了做演示,在MySql数据库针对页面、布局和模块分别建表,并插入演示的数据,如下所示

     

    页面:

     

    页面与布局关系 

     

     布局与模块关系

     

    模块与业务表关系,以公告资讯为例

     

     

    现在数据都准备好了,就可以输出页面了,此处举例说明如何实现

    package com.yli.cshop.service.impl;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import com.yli.cshop.bean.BaseModule;
    import com.yli.cshop.bean.Layout;
    import com.yli.cshop.bean.Page;
    import com.yli.cshop.service.BaseModuleService;
    import com.yli.cshop.service.PageService;
    import com.yli.sample.base.ServiceImplBase;
    
    public class PageServiceImpl extends ServiceImplBase implements PageService{
    	
    	public  Map<String, BaseModuleService> moduleServiceCache;
    	
    	public void setModuleServiceCache(
    			Map<String, BaseModuleService> moduleServiceCache) {
    		this.moduleServiceCache = moduleServiceCache;
    	}
    
    	
    	public String loadPage(){
    		
    		Map<String, Object> param = new HashMap<String, Object>();
    		param.put("pageId", 100001);
    		
    		// 根据页面ID查询Page对象
    		Page page = sopDalClient.queryForObject("com.yli.cshop.layout.query_page", param, Page.class);
    		
    		// 根据页面ID查询:该页面包含的布局列表,根据布局序号升序排序
    		List<Layout> layoutList = sopDalClient.queryForList("com.yli.cshop.layout.query_layout_list", param, Layout.class);
    		
    		if(null != layoutList && !layoutList.isEmpty()) {
    			// 设置布局列表到页面对象中
    			page.setLayoutList(layoutList);
    			
    			// 根据布局ID查询:每个布局包含的模块列表,根据模块所在布局序号升序排序
    			for(Layout layout : layoutList) {
    				param.put("layoutId", layout.getLayoutId());
    				List<BaseModule> moduleList = sopDalClient.queryForList("com.yli.cshop.layout.query_module_list", param, BaseModule.class);
    				if(null != moduleList && !moduleList.isEmpty()) {
    					// 设置模块列表到页面对象中
    					layout.setModuleList(moduleList);
    					for(BaseModule module : moduleList) {
    						// 查询每个模块应该包含的业务内容,此处处理比较复杂
    						BaseModuleService baseModuleService =  moduleServiceCache.get(module.getModuleServiceBean());
    						Object moduleContent = baseModuleService.getModuleContent(module.getModuleId());
    						// 将模块的业务内容设置到模块实例中
    						module.setModuleContent(moduleContent);
    					}
    				}
    			}
    		}
    		
    		// toString() 其实就是使用FreeMarker的process方法达到[数据]+[模板]=[输出]的目的
    		// 这个pageContent就是整个页面的内容了,只要显示在前端即可
    		// 不管你用Jstl来读取,还是ajax来读取,都可以
    		String pageContent = page.toString();
    		System.out.println(pageContent);
    		
    		// 如果觉得组件类依赖了FreeMarker,被侵入,则也可以抽象出输出页面的工厂类
    		// page 就只作为保存[数据]和[模板]的功能来设计
    		// String pageContent = PageFactory.generatePage(page);
    		
    		return pageContent;
    	}
    }
    

     

     

    最后就是效果图了,很简答的一个

     

     

     

     


     

    展开全文
  • 装饰模式是一种结构型设计模式,这种设计模式可以动态地拓展原有对象的功能,代替类的继承。下面举个例子。 //车辆类,抽象类 abstract class Vehicle{ abstract void run(); } //汽车类 class Car extends ...

    上一篇讲述如何优化Padding使其在树形布局内部拖拽时也能生效。本篇讲解如何通过两种设计模式来极大地增加布局的拓展性。

    前期准备

    在真正开始之前我们首先要熟悉两种设计模式。

    1.策略模式

    策略模式可以将一些具有相似特征的算法提取出来变成一个接口,这样就可以根据需要动态地实现算法的替换。说得有些抽象,举个例子。

    比如一个类要用到排序,根据排序量大小、初始状态的不同,我们可以在快排、冒泡、归并等等算法进行选择。但是常规的方式是通过if-else分支来实现。问题就来了,如果现在要添加一种排序算法,就不得不在代码里再添加一个else代码块,这是与开闭原则相悖的。代码如下:

    class ClassA{
    	void sort(int arr[]){
    		if(...){
    			//快排
    		}else if(...){
    			//冒泡
    		}else if(...){
    			//归并
    		}else if(...){
    			//新添加的排序算法
    		}
    	}
    }
    

    想想看,如果大多数情况代码都要跳到第四种算法来进行排序,那就避免不了大量的比较判断。利用策略模式就能为了解决上述两种问题。

    无论是快排、冒泡还是归并,它们都有共同操作那就是给数组排序,所以可以抽象出一个排序类(或接口)。

    public interface SortStrategy{
    	void sort(int arr[]);
    }
    

    具体是怎么排的就看它们自己的实现方式了。

    public class QuickSort extends SortStrategy{
    	public void sort(int arr[]){
    		...
    		//算法用时方恨少,哎...忘的差不多了
    	}
    }
    
    public class BubbleSort extends SortStrategy{
    	public void sort(int arr[]){
    		...
    	}
    }
    
    public class MergerSort extends SortStrategy{
    	public void sort(int arr[]){
    		...
    	}
    }
    

    根据需要使用具体的策略就行了,就算要添加新的排序算法,也不需要再修改ClassA了,而且还能避免大量的if-else分支判断。

    class ClassA{
    	void sort(int arr[],SortStrategy sortStrategy){
    		sortStrategy.sort(arr);
    	}
    }
    
    2.装饰模式

    装饰模式是一种结构型设计模式,这种设计模式可以动态地拓展原有对象的功能,代替类的继承。下面举个例子。

    //车辆类,抽象类
    abstract class Vehicle{
    	abstract void run();
    }
    
    //汽车类
    class Car extends Vehicle{
    	@Override
    	void run(){
    		System.out.print("run as a car");
    	}
    }
    
    //越野车类
    class SUV extends Vehicle{
    	@Override
    	void run(){
    		System.out.print("run as a suv");
    	}
    }
    
    //加装涡轮增压器的车辆类
    class JetVehicle extends Vehicle{
    	Vehicle mVehicle ;
    
    	public JetVehicle(Vehicle sourceVehicle ){
    		mVehicle  = sourceVehicle ;
    	}
    
    	@Override
    	void run(){
    		mVehicle.run();
    		System.out.print(" with jet engine");
    	}
    }
    
    //全自动车辆类
    class AutoVehicle extends Vehicle{
    	Vehicle mVehicle ;
    
    	public AutoVehicle (Vehicle sourceVehicle ){
    		mVehicle  = sourceVehicle ;
    	}
    
    	@Override
    	void run(){
    		mVehicle.run();
    		System.out.print(" which is automatic");
    	}
    }
    

    现在有如下三种客户需求。A客户想要一辆加装涡轮增压器的汽车,B客户想要一辆能自动驾驶的越野车,C客户想要一辆即加装涡轮增压器又能自动驾驶的汽车。看看装饰模式下的代码实现。

    //A客户的需求
    Vehicle vehicleA = new JetVehicle(new Car());
    
    //B客户的需求
    Vehicle vehicleB = new AutoVehicle (new SUV());
    
    //C客户的需求
    Vehicle vehicleC = new AutoVehicle (new JetVehicle(new Car()));
    

    其实我们发现,ABC客户的需求无非就是在 [汽车,SUV] 和 [涡轮增压器,自动驾驶]中自由搭配,可如果使用继承的方式来实现,就不得不为每一种不同的需求创建一个新的类,随着需求多了,继承方式就会变得很不灵活。

    3.工厂方法模式

    回到刚刚的例子,现在需要制造多辆带有涡轮增压器且能自动驾驶的汽车。原先的做法如下:

    Vehicle vehicle1 = new Car();
    Vehicle vehicle1WithJet = new JetVehicle(vehicle1);
    Vehicle autoVehicle1 = new AutoVehicle(vehicle1WithJet );
    
    Vehicle vehicle2 = new Car();
    Vehicle vehicle2WithJet = new JetVehicle(vehicle2);
    Vehicle autoVehicle2 = new AutoVehicle(vehicle2WithJet );
    
    ...
    
    Vehicle vehicleN = new Car();
    Vehicle vehicleNWithJet = new JetVehicle(vehicleN);
    Vehicle autoVehicleN = new AutoVehicle(vehicleNWithJet);
    

    每次创建带有涡轮增压器且能自动驾驶的汽车都要进行复杂的改装(初始化),这就是装饰模式的不足之处,把创建过程变复杂了。为了能够简化创建流程,这里可以使用工厂方法模式。工厂方法模式是一种创建型设计模式,它适用于创建过程复杂的对象,调用者不必过于关注对象的创建过程。如下是工厂模式的代码。

    abstract class AbsVehicleFactory{
    	abstract Vehicle createVehicle();
    }
    
    class AutoJetVehicleFactory extends AbsVehicleFactory{
    	Vehicle createVehicle(){
    		Vehicle vehicle = new Car();
    		Vehicle vehicleWithJet = new JetVehicle(vehicle);
    		Vehicle autoVehicle = new AutoVehicle(vehicleWithJet );
    		return autoVehicle;
    	}
    }
    

    创建一个专门制造 带涡轮增压器且能自动驾驶汽车 的工厂后,创建这种类型的车辆就简单多了。

    AutoJetVehicleFactory factory = new AutoJetVehicleFactory();
    Vehicle vehicle1 = factory.createVehicle();
    Vehicle vehicle2 = factory.createVehicle();
    ...
    Vehicle vehicleN = factory.createVehicle();
    

    利用策略模式和装饰模式改写连接线绘制类

    现在想要给树形布局添加绘制结点外框的功能。想想看,无论是绘制连接线还是绘制结点的外框,其实它们都是树形布局的点缀。既然是这样,我们就可以把它们都抽象成点缀类,点缀类无非是在布局上画点东西,至于画什么,就看子类的具体实现了。再看看下面这张图片。
    在这里插入图片描述
    不同的连接线可以搭配不同的结点外框,例如第一种线搭配第一种外框,就得到如下的效果。
    在这里插入图片描述
    代码实现如下。

    /**
         * TreeLayout专用的外间距参数类,用于记录布局参数
         */
        public static class LayoutParams extends MarginLayoutParams{
            public LayoutParams(Context c, AttributeSet attrs) {
                super(c, attrs);
            }
    
            public LayoutParams(ViewGroup.LayoutParams source) {
                super(source);
            }
        }
    
        /**
         * 点缀绘制器,TreeLayout通过这个类的子类来绘制点缀内容
         */
        public static abstract class NodeDectorDrawer {
            private NodeDectorDrawer mSourceDector;
    
            public NodeDectorDrawer(NodeDectorDrawer sourceDector){
                mSourceDector = sourceDector;
            }
    
            /**
             * 绘制自身树布局的点缀
             * @param canvas 绘制点缀的画布
             * @param paint 绘制点缀的画笔
             * @param start 点缀的起点控件的区域,即父结点控件所在区域
             * @param end 点缀的终点控件的区域,即子结点控件所在区域
             * @param direction 树的方向
             *                  参考{@link #DIRECTION_LEFT_TO_RIGHT,
             *                       @link #DIRECTION_RIGHT_TO_LEFT,
             *                       @link #DIRECTION_UP_TO_DOWN,
             *                       @link #DIRECTION_DOWN_TO_UP}
             */
            protected abstract void onDrawDecorator(Canvas canvas, Paint paint, Rect start, Rect end, int direction);
    
            /**
             * 绘制自身树布局的点缀
             * @param canvas 绘制点缀的画布
             * @param paint 绘制点缀的画笔
             * @param start 点缀的起点控件的区域,即父结点控件所在区域
             * @param end 点缀的终点控件的区域,即子结点控件所在区域
             * @param startView 点缀的起点控件
             * @param endView 点缀的终点控件
             * @param direction 树的方向
             */
            public void drawDecorator(Canvas canvas, Paint paint, Rect start, Rect end,View startView,View endView,int direction){
                if(mSourceDector != null){
                    mSourceDector.drawDecorator(canvas,paint,start,end,startView,endView,direction);
                }
    
                if(skipThisDraw(startView,endView)){
                    return;
                }
    
                onDrawDecorator(canvas,paint,start,end,direction);
            }
    
            /**
             * 跳过本树布局的点缀绘制
             * @param startView 点缀的起点控件
             * @param endView 点缀的终点控件
             * @return 是否跳过本树布局的点缀绘制
             */
            public boolean skipThisDraw(View startView,View endView){
                return false;
            }
        }
    

    由于篇幅问题,点缀绘制器的实现类代码这里就不贴出了,文章最后有Github项目地址。自带的实现类有如下几个。
    DirectLineDrawer:直线绘制器
    DocumentLineDrawer:折线绘制器
    CurveDrawer:曲线绘制器
    RectFrameDrawer:方框绘制器
    ConnectPointDrawer:连接点绘制器

    来看看具体使用,通过装饰模式搭配出一种绘制树形布局的点缀内容。

    ...
    DocumentLineDrawer documentLineDrawer = new DocumentLineDrawer(6,treeView.getLevelInterval(),Color.WHITE);
    RectFrameDrawer rectFrameDrawer = new RectFrameDrawer(documentLineDrawer,12,Color.WHITE);
    ConnectPointDrawer connectPointDrawer = new ConnectPointDrawer(rectFrameDrawer,12,Color.WHITE);
    
    DocumentLineDrawer documentLineDrawer1 = new DocumentLineDrawer(6,treeView1.getLevelInterval(),Color.WHITE);
    RectFrameDrawer rectFrameDrawer1 = new RectFrameDrawer(documentLineDrawer1,12,Color.WHITE);
    ConnectPointDrawer connectPointDrawer1 = new ConnectPointDrawer(rectFrameDrawer1,12,Color.WHITE);
    
    treeView.setDecorDrawer(connectPointDrawer);
    treeView1.setDecorDrawer(connectPointDrawer1);
    ...
    

    在这里插入图片描述
    换个搭配试试。
    在这里插入图片描述
    再写几种连接线类和外框类,搭配上的选择顿时就变多了(这时突然想起了高中学的排列组合)。

    利用工厂方法模式简化绘制器创建

    随着我们的绘制类越来越多,要搭配出一个我们想要的绘制器就变得复杂起来了。为了简化这些复杂的创建流程,我们可以采用工厂方法模式。

    首先写一个绘制器工厂的抽象类。

    public abstract class AbsDecoratorFactory {
        public abstract TreeLayout.NodeDecoratorDrawer createDecorator();
    }
    

    接下来根据实际需要写一个对应的实现类即可。

    public class ClassicDecoratorFactory extends AbsDecoratorFactory {
        private int mLineWidth;
        private int mRadius;
        private int mColor;
        private int mInterval;
    
        public ClassicDecoratorFactory(int lineWidth, int radius, int interval, int color) {
            mLineWidth = lineWidth;
            mRadius = radius;
            mColor = color;
            mInterval = interval;
        }
    
        @Override
        public TreeLayout.NodeDecoratorDrawer createDecorator() {
            DirectLineDrawer directLineDrawer = new DirectLineDrawer(mLineWidth,mColor);
            RectFrameDrawer rectFrameDrawer = new RectFrameDrawer(directLineDrawer,mLineWidth,mColor);
            ConnectPointDrawer connectPointDrawer = new ConnectPointDrawer(rectFrameDrawer,mRadius, mColor);
            return connectPointDrawer;
        }
    }
    

    可以看到,复杂的绘制器组合搭配过程就被藏在了工厂类里,调用者只需少量代码即可生成目标绘制器。

    ClassicDecoratorFactory factory = new ClassicDecoratorFactory(6,12,treeView.getLevelInterval(),Color.WHITE);
    
    treeView.setDecorDrawer(factory.createDecorator());
    treeView1.setDecorDrawer(factory.createDecorator());
    

    最后

    利用装饰模式和工厂方法模式来优化代码确实是很好的选择,装饰模式增加了树形布局的拓展性,工厂方法模式简化了装饰模式的复杂搭配过程。

    由于代码重构,前面几篇文章讲到的连接线绘制的代码被改动了。由于篇幅原因,文章很遗憾没能贴出全部代码,感兴趣的朋友可以到Github项目中阅读完整代码。喜欢这个布局控件的话可以star一下以示鼓励。

    下一篇讲解如何给树形布局添加遍历监听功能。

    展开全文
  • newInstance()方法这个是一种“静态工厂方法",需要的时候来调用去实例化; 让我们在初始化和设置一个新的fragment的时候免得去一一创建 。  调用MyFragment.newInstance(index)去动态创建Fragment ...
  • Qt编写可视化大屏电子看板系统1-布局方案

    万次阅读 多人点赞 2019-05-12 16:13:31
    布局方案在整个数据可视化大屏界面电子看板系统中,是除了基础功能以外的核心功能之一,只有具备了布局方案这个功能,才能让用户随意调整自己想要的布局,保存成自定义名称的布局配置文件,这样就大大增加了灵活性,...
  • 智慧工厂是现代工厂信息化发展的新阶段,是在数字化工厂的基础上利用物联网技术和设备监控技术加强对信息的管理和服务;达到对产销流程的清楚掌握、提高对生产过程的可控性、减少对生产线人工的干预、达到即时正确地...
  • C# 工厂

    千次阅读 2013-03-17 22:08:21
    (转载)C#抽象工厂模式的几种实现方法及比较 Posted on 2008-12-05 19:45 chefZ 阅读(5127) 评论(2) 编辑 收藏 引用 C#抽象工厂模式的几种实现方法及比较 转载地址:...
  • Rocha所写,文中对Android中常用的四种自定义布局方案进行了很好地分析,并结合这四种Android自定义布局方案所写的示例项目讲解了它们各自的优劣以及四种方案之间的比较。看完这篇文章,也让我对Android 自定义布局...
  • 布局对象中请求到布局属性后,它将会实例化所有视图,并将对应的属性应用到每个视图上去。   注意!这个方法涉及到所有类型的视图,也就是 cell , supplementary views 和 decoration views 。一个幼稚...
  • 数字化工厂解决方案

    千次阅读 2018-09-28 15:30:52
    装备行业数字化工厂 ... SVDF是华天软件完全拥有自主知识产权的数字化工厂规划布局、三维展示、虚拟仿真的系统。SVDF建立在数字化模型基础上,通过输入数字化工厂的各种制造资源、工艺数据、CAD数据等建立...
  • 简单的说,LayoutInflater就是是一个用来解析xml布局文件的类。今天该篇文章将对LayoutInflater类进行分析,内容包括: 1. LayoutInflater在哪里创建的 2. 如何获取LayoutInflater 3. 视图的创建过程(xml转换成...
  • 能够新建布局,也是数据可视化大屏界面电子看板系统中的必备功能之一,新建布局这样的功能一般做到右键菜单中,单击新建布局菜单,弹出输入框要求输入新的布局的名称,为了更符合国情,直接支持中文名称,保存成配置...
  • 1.新建名为domxml的项目 2.新建user_item.xml的自定义布局文件 android:layout_width="match_parent" a
  • 布局另存是数据可视化大屏界面电子看板系统中的额外功能之一,主要用于有时候用户需要在现有布局上做个微调,然后直接将该布局另存为一个布局配置文件使用,可以省略重新新建布局重新来一次大的调整的工作,此功能...
  • 精益化工厂布局2.自动化生产线导入3.信息化系统运用4.智能工厂打造精工智能工厂规划展望数字化工厂解决方案—斑彰(会员单位)透明化车间典型案例呈现 2017年11月11日,由我会会员单位上海斑彰信息技术有限公司总...
  • python 实现 工厂模式

    千次阅读 2018-09-03 15:51:21
    工厂设计模式中,客户端①可以请求一个对象,而无需知道这个对象来自哪里;也就是,使用哪个类来生成这个对象。工厂背后的思想是简化对象的创建。与客户端自己基于类实例化直接创建对象相比,基于一个中心化函数来...
  • 删除布局内的控件

    千次阅读 2013-05-16 17:39:00
    基于某些原因,我们可能希望往一个布局动态删除和添加新的元素,添加我们都能很清楚得看到addWidget这个接口.删除的话官方也有示例: QLayoutItem *child; while(child=this->ui->centralWidget->layout()-...
  • Qt删除布局内的控件

    万次阅读 2016-03-07 17:50:42
    基于某些原因,我们可能希望往一个布局动态删除和添加新的元素,添加我们都能很清楚得看到addWidget这个接口.删除的话官方也有示例: QLayoutItem *child; while(child=this->ui->centralWidget->layout()...
  • 应当进行科学的厂房布局规划,在满足生产工艺要求,优化业务流程的基础上,提升物流效率,提高工人工作的舒适程度。智能工厂的推进需要企业的IT部门、自动化部门、精益推进部门和业务部门的通力合作。制造企业应当...
  • 作为工业4.0的最大主题,智能工厂可谓贯穿产业升级全过程。本文对智慧工厂进行了详细的解读,并对中国智慧工厂发展现状和空间进行全面的分析。智慧工厂简介自德国政府于2013年在汉诺威工业博览会上正式公布,“工业...
  • 2018广州市物流技术与应用协会周年庆典暨中国数字化工厂应用与发展大会在今天于广州隆重举行。科技自动化联盟秘书长、智慧工厂研究院院长王健受邀参加本次活动并发表演讲。现分享王总在活动现场的演讲PPT。人工智能...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,435
精华内容 4,974
关键字:

工厂动态布局