【问题标题】:Flavor specific code风味特定代码
【发布时间】:2017-08-10 06:10:26
【问题描述】:

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

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

我可以使用编译依赖项

barCompile 'com.framer:frame_me:1.1'

但是片段和它的 XML 呢?我不想为两种风格的片段编写 2 个变体,因为我不想在 2 个地方维护相同的代码。


我脑海中的一个可能的想法(可能是一个糟糕的想法)是这样的:

  1. 将 XML 元素移动到 bar 源集中的单独文件中。在foo 源集中添加ViewStub 元素,同名。现在使用 include 在片段 XML 中包含此 XML 文件
  2. 添加一个接口来处理main 源集中的Frame 视图。在foo 源集中添加一个空实现,在bar 源集中添加一个。这样,所有逻辑都可以保留在 bar 中,而所有常见逻辑都保留在 main 源集中。

仅仅为了编写特定于风味的代码和 xml,这一切听起来都需要大量的工作。

【问题讨论】:

  • 为什么不为 Frame 扩展一个视图,在 main.xml 中有这个视图,但是 GONE 并且只有在运行风味时才启用它?您可以在 View 的新子类中拥有一个额外的 xml
  • barCompile! Frame 甚至不存在于 foo 风格中,这将引发错误。还是我没有正确理解您的评论?
  • 你的xml也需要依赖吗?
  • 是的,Frame 类存在依赖关系。我无法在不存在的 xml 中添加视图。
  • 您可以在主 xml 中为 Frame 使用容器 ViewGroup 子类。但它仍然倾向于您提出的解决方案。您将必须实现此 ViewGroup 子类两次,每种风格一次。

标签: android android-flavors


【解决方案1】:

FrameLayout 容器替换XML 中的Frame 标记怎么样?

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

这类似于您的第一种方法,但无需维护单独的资源集。而且看起来很合理,无论如何你都会有一些特定于风格的 java 代码。

【讨论】:

  • 但是如何在不将整个 Fragment 复制为 2 种风格的情况下,将特定风格的 Java 代码分开?
  • 您可以将部分代码移动到帮助程序类中,将该文件复制到第二种风格并在那里进行修改。但是您必须拥有两种风格的完整源文件。
  • 所以基本上和我提议的一样,只是有点不同。我想这是唯一的方法。伤心。
  • 这似乎并不过分。您可以通过创建一个非常小的帮助类来最大程度地减少工作量,该类仅包含特定于风味的代码。
  • 是的,但是随着 2 种风格的不同之处变得越来越不同,但仍然共享许多通用代码,这变得越来越复杂。我修复了我的代码分钟,并将所有通用代码移动到一个基类,并以 2 种风格扩展了 2 个类,留下一个完全空的。看起来它将来也会很好地工作。我会等几天再接受这个答案,以防出现更好的答案
【解决方案2】:

你只需要抽象。由于资源是使用 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() 设置布局文件。如果您有更多差异,您只需将它们处理到每个抽象方法中。基类可以融入公共代码、实现和每种风格。如果你不需要从外部设置任何行为代码,你可以去掉接口。

【讨论】:

  • 我做了类似的事情,没有使用这么多抽象方法,而是简单地覆盖了一些方法来添加功能并动态添加视图。
  • 系统如何知道为特定风格运行什么代码?我假设,您在检查当前风味后加载特定的片段实现?
【解决方案3】:

build.gradle sourceSets 选项是什么? 您可以将 Fragment 和 XML 放在 bar 文件夹中,然后设置:

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

【讨论】:

  • 我知道源集,我的问题是如何在不复制大量代码的情况下做到这一点 2 风味 java 源集。因为我不喜欢重复代码
猜你喜欢
  • 1970-01-01
  • 2015-03-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 2020-05-05
  • 1970-01-01
相关资源
最近更新 更多