Android 风味特定代码

Android 风味特定代码,android,android-flavors,Android,Android Flavors,考虑一个带有片段的活动MainFragment。片段有一些复杂的布局层次结构和一个视图组Frame,它来自一个库com.framer:Frame\u me:1.1 如果我有两种风格foo和bar,我希望这个Frame只存在于bar风格中,而不存在于foo中,那么XML元素java代码和依赖项。我该怎么做 我可以使用 barCompile 'com.framer:frame_me:1.1' 但是片段和它的XML呢。我不想用两种风格编写两个变体,因为我不想在两个地方维护相同的代码 我脑海中一个可

考虑一个带有片段的活动
MainFragment
。片段有一些复杂的布局层次结构和一个视图组
Frame
,它来自一个库
com.framer:Frame\u me:1.1

如果我有两种风格
foo
bar
,我希望这个
Frame
只存在于
bar
风格中,而不存在于
foo
中,那么XML元素java代码和依赖项。我该怎么做

我可以使用

barCompile 'com.framer:frame_me:1.1'
但是片段和它的XML呢。我不想用两种风格编写两个变体,因为我不想在两个地方维护相同的代码


我脑海中一个可能的想法(可能是个坏主意)是:

  • bar
    源集中的单独文件中移动XML元素。在
    foo
    源集中添加具有相同名称的
    ViewStub
    元素。现在使用
    include
    在片段XML中包含这个XML文件
  • main
    源代码集中添加一个界面来处理
    Frame
    视图。在
    foo
    源集中添加一个空实现,在
    bar
    源集中添加一个空实现。这样,所有逻辑都可以保留在
    条中
    ,而所有公共逻辑都保留在
    main
    源集中

  • 仅仅为了编写特定于味道的代码和xml,这听起来是一项非常艰巨的工作。

    FrameLayout
    容器替换xml中的
    Frame
    标记如何

    然后在
    bar
    flavor的源代码中,您可以实例化
    Frame
    并说
    container.addView(Frame)
    。而
    foo
    flavor将不引用
    Frame
    类,并将忽略容器


    这与第一种方法类似,但不必维护单独的资源集。这似乎是合理的,您将有一些特定于风格的java代码。

    build.gradle sourceset选项是什么? 您可以将片段和XML放入bar文件夹中,然后设置:

    android {
        productFlavors {
             ...
        }
        sourceSets {
             bar.java.srcDirs = ['src/bar/java']
             bar.res.srcDirs = ['src/bar/res']
        }
    }
    

    你只需要抽象。由于资源是使用R类中的整数索引标识的,因此可以使用int变量作为布局文件的占位符,并且如果在活动布局中搜索了布局元素ID,则可以循环使用公共元素。首先,创建一个公共片段类,包含所有公共元素:

    public abstract class BaseFlavorFragment extends Fragment {
    
    /*Define an interface for whatever code the fragment may need from the outside and a member for keeping reference of that. You can also use the host activity, this is just for flexibility*/
        public interface whateverThisDoes{
            void do();
        }
    /*All the common fragment members go here, as protected so you can reach them from every subclass*/
        protected TextView title;
        protected Button mainButton;
        protected whateverThisDoes listener;
    
        public void setWhateverThisDoes(whateverThisDoes listener){
            this.listener = listener;
        }
    /*Finally, create a int variable that will hold the reference to the layout file you need to use. you will set this in every flavor using the setContainer method.*/
        protected int layout = 0;
    
        /*this will allow you to select which XML to use
    layout = R.layout.flavorlayout*/
        public abstract setContainer();
    
        /*Use this method to inflate any flavor members, like the Frame you mentioned*/
        public abstract void inflateComponents();
        /*Use this to set listeners, data, or anything the flavor controls do*/
        public abstract void setBehaviors();
    
        /*Set here anything the common controls do*/
        protected void setCommonBehaviors(){
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //whatever                
                }
            });
            setBehaviors();
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setContainer();
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
           super.onCreateView(inflater, container, savedInstanceState);
           View view = inflater.inflate(layout, container, false);
           /*Inflate common components*/
           title =  (TextView) root.findViewById(R.id.title);
           button =  (Button) root.findViewById(R.id.button);
           /*inflate flavor components, if there's any*/
           inflateComponents(); 
           /*assign data, listeners, whatever the flavor controls do*/
           setBehaviors();          
           return view;
        }
    }
    

    现在,您可以为Foo和Bar创建一个实现。如果唯一的区别是布局文件,请将所有内容放入基类,并使用setContainer()设置布局文件。如果您有更多的差异,您只需要在每个抽象方法中处理它们。基类可以存在于公共代码、实现和每种风格中。如果不需要从外部设置任何行为代码,可以删除该接口。

    为什么不扩展框架的视图,将该视图放在main.xml中,但只有在flavor上运行时才启用它?您可以在View
    barCompile
    的新子类中增加一个额外的xml!
    框架
    甚至不存在于
    foo
    flavor中,这将抛出一个错误。或者我没有正确理解您的评论?您的xml也需要依赖项吗?是的,
    Frame
    类存在于依赖项中。我无法添加不存在的xml视图。可以在主xml中为框架使用容器ViewGroup子类。但它仍然倾向于你提出的解决方案。您必须实现这个ViewGroup子类两次,每种风格一次。但是我如何保持特定于风格的Java代码独立,而不在两种风格中复制整个片段?您可以将部分代码移动到帮助器类,将该文件复制到第二种风格,然后在那里修改它。但是你必须有两种风格的完整源文件,所以本质上和我建议的一样,只是有点不同。我想这是唯一的办法。可悲的是,这似乎并不是压倒性的。您可以通过创建一个非常小的帮助器类来最小化工作量,该类只包含特定于味道的代码。是的,但是随着两种味道的不同变得越来越不同,但仍然共享许多常见代码,这会变得越来越复杂。我修复了几分钟前的代码,将所有常用代码移到一个基类,并将2个类扩展为2种风格,使其中一个完全为空。这看起来在将来也能很好地工作。在接受这个答案之前,我将等待几天,以防有更好的答案显示我了解源代码集,我的问题是如何在不复制2个风格java源代码集中的大量代码的情况下实现这一点。因为我不喜欢代码重复,所以我做了类似的事情,不是用这么多抽象方法,而是简单地重写一些方法来动态添加功能和视图。系统如何知道为特定的风格运行什么代码?我假设,您在检查当前的风格之后加载特定的片段实现?