Android中的编码风格:何时应该使用样式而不是内联属性?

时间:2015-06-28 17:00:57

标签: android android-layout coding-style

我不确定在Android中开发界面的最佳方式是什么。 通过将内联属性移动到样式文件来清理布局文件是否更好?据我所知,在HTML中最好在HTML中使用类和ID,并在style.css文件中使用它们。安卓怎么样?

1 个答案:

答案 0 :(得分:8)

我发现了这个,也许它可以帮助别人。

何时使用样式

我们必须解决的第一个问题很简单:何时应该使用样式而不是内联属性?

规则#1:当多个视图在语义上相同时使用样式。

最好通过几个例子来说明这条规则:

  • 您正在创建一个计算器。每个按钮看起来都一样,所以创建一个`CalculatorButton`样式是有意义的。
  • 您有多个具有多种文字格式的屏幕 - 比如标题,子标题和文字。你可以通过创建`Header`,`Subheader`和`Text`样式来统一它们的外观。
  • 您的应用程序中都有缩略图。你希望它们看起来都一样。 “缩略图”风格诞生了。

所有这些示例中的共同点是,这些Views不只是使用相同的属性 - 它们在整个应用中扮演相同的角色。现在,当您想要调整这些Views中的任何一个的外观时,您只需编辑样式并立即更改它们。它可以节省您的时间和精力,并使Views保持一致。

想要节省更多工作?使用资源引用!

规则#2:在适当的时候使用样式中的引用。

您可以通过这种方式定义样式:

<style name="MyButton">
<item name="android:minWidth">88dp</item>
<item name="android:minHeight">48dp</item>
</style>

如果您希望minWidth根据屏幕尺寸变化,该怎么办?您可以在每个屏幕尺寸上复制一次样式(例如,sw600dpsw900dp),但您也必须复制minHeight属性。如果您希望两个属性发生变化,该怎么办?突然间,你到处都有大量的MyButtons被定义,每个都重复了所有其他属性。这是灾难的秘诀;很容易忘记在多个副本之一中更改一个属性。

样式只是一系列属性的别名。定义这样的风格要容易得多:

<style name="MyButton">
<item name="android:minWidth">@dimen/button_min_width</item>
<item name="android:minHeight">@dimen/button_min_height</item>
</style>   

现在,您只需修改每个资源限定符的单个属性即可。考虑重复布局只是为了改变肖像与景观中View的宽度,这是荒谬的。您可以使用维度。这同样适用于款式。

我不是故意暗示你总是在样式中使用资源引用;如果您需要在资源限定符上切换多个值,那么您应该使用它。

这并不是说有时您不需要跨资源限定符复制样式,但您可以将其保持在最低限度。通常,这样做的唯一原因是平台更改(例如,从paddingLeftpaddingRight更改为paddingStartpaddingEnd)。

多种样式

如果您可以将多种样式应用于单个View,如CSS。

,那就太棒了

你不能。遗憾。

但在某些情况下,你可以得到多种风格的近似值。

规则#3:使用主题调整默认样式。

主题提供了定义许多标准小部件的默认样式的方法。例如,如果要为应用程序定义默认按钮,则可以执行以下操作:

<style name="MyTheme">
<item name="android:buttonStyle">@style/MyButton</item>
</style>

如果你只是调整默认风格,唯一棘手的部分就是找出你风格的父母;您希望它与设备的相应主题相匹配,但这会因操作系统版本而异。

如果您正在使用AppCompat主题,则应将其样式用作父级,因为它们也可以处理不同平台之间的差异。例如,它们具有Spinner样式:

<style name="MySpinner" parent="Widget.AppCompat.Spinner" />

如果AppCompat中没有样式(或者你没有使用它),问题就会变得有点棘手,因为你需要父母根据主题进行切换。以下是正常使用Holo的自定义Button样式示例,但适当时使用材质。

您将其放在/values/values.xml

<style name="ButtonParent" parent="android:Widget.Holo.Button />

<style name="ButtonParent.Mine">
<item name="android:background">@drawable/my_bg</item>
</style>

然后,在/values-v21/values.xml

<style name="ButtonParent" parent="android:Widget.Material.Button />   

设置正确的父级将确保您的应用和平台的一致性。

如果你真的想要定义所有必要的属性(而不仅仅是调整默认值),你可以完全跳过育儿。

规则#4:尽可能使用文字外观。

TextAppearance允许您为一些最常修改的文本属性合并两个样式。看看你的所有风格:其中有多少修改文字的外观?在这些情况下,您只需修改TextAppearance

首先,您需要定义TextAppearance

<style name="MyTextAppearance" parent="TextAppearance.AppCompat">
<item name="android:textColor">#0F0</item>
<item name="android:textStyle">italic</item>
</style>

请注意我如何设置父级 - 文本外观不会合并,因此您需要确保定义所有属性。您可以使用任何适当的TextAppearance作为父级。

现在您可以在TextView

中使用它
<TextView
style="@style/MyStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/MyTextAppearance" />

请注意,我仍然可以为这个TextView应用一种样式,让我为一个视图获得惊人的两种风格!不如真正的多种风格,但我会采取我能得到的。

您可以在任何扩展TextAppearance的类中使用TextView。这意味着EditTextButton等都支持文字样式。

常见陷阱

我在使用样式时一直都在解释。不幸的是,从长远来看,很容易滥用款式会对你造成伤害。这里有一些要避免的反模式。

规则#5:如果只使用一次,请不要创建样式。

样式是一个额外的抽象层。它增加了复杂性。您必须查找样式才能查看它们应用的属性。因此,除非您在多个地方使用该风格,否则我认为没有理由使用它们。

打开布局时,您更愿意看到这个:这个?

<TextView style="HelloWorldTextView" />

还是这个?

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

如果您需要,如此简单可以创建样式。不要过早计划。

规则#6:不要因为多个视图使用相同的属性而创建样式。

使用样式的主要原因是减少重复属性的数量,对吧?为什么不在多个视图使用相同属性时使用样式?

这种态度的问题在于,那些Views,如果它们不在相同的上下文中使用,最终可能希望在它们的外观上有所不同。此时,您的基本样式变得难以编辑而没有意外的副作用。

想想这个场景:你有一些TextViews相同的文字外观和背景。你想,&#34;嘿,我会创造一种风格,减少代码重复。&#34;一开始一切都很笨拙,但最终你想要调整一些TextViews看起来如何。问题是,到目前为止,所有地方都使用了该样式,因此您无法在没有附带损害的情况下对其进行编辑

好吧,你说 - 我只是直接在布局XML中覆盖样式。问题解决了。然后又发生了。然后再次。最终这种风格毫无意义,因为你不得不在任何地方覆盖它。它最终会增加额外的工作,而不是让生活更轻松。

这就是我在规则#1中指定当Views 语义相同时应使用样式的原因。这样可以确保在更改样式时,您确实 希望每个View使用样式进行更改。

隐式与显式育儿

样式支持父级,其中子样式采用父样式的所有属性。如果他们不这样做,那将是相当有限的。

假设我希望应用中的每个Button看起来都一样,所以我制作了ButtonStyle。后来,我决定Buttons的一半应该看起来略有不同 - 通过育儿,我可以创建ButtonStyle.Different,获得基本风格+调整。

事实证明,有两种方式可以隐式和明确地定义父母:

<!-- Our parent style -->
<style name="Parent" />

<!-- Implicit parenting, using dot notation -->
<style name="Parent.Child" />

<!-- Explicit parenting, using the parent attribute -->
<style name="Child" parent="Parent" />

够简单吧?但是,当我们用两种方法定义父母时,你认为在这里发生了什么?

<style name="Parent.Child" parent="AnotherParent" />

如果您回答说这个风格有两个父母,那么错误。事实证明它只有一个父:AnotherParent

每种样式只能有一个父项,即使有两种方法可以定义它。显式父项(使用属性)优先。这引出了我的下一条规则:

规则#7:不要混合隐式和显式育儿。

混合这两者是混淆的一个秘诀。假设我有这个布局:

<Button
style="@style/MyWidgets.Button.Awesome"
android:layout_width="match_parent"
android:layout_height="match_parent" />

但事实证明我的风格是这样定义的:

<style name="MyWidgets.Button.Awesome" parent="SomethingElse" />

即使看起来,我的Button也基于MyWidgets.Button,但事实并非如此!样式名称具有误导性,发现它的唯一方法是做额外的工作并深入挖掘你的样式文件。

常见的诱惑是继续使用带有明确父级的点符号,以便您的样式看起来与层次结构相关:

<style name="MyButton" parent="android:Widget.Holo.Button" />
<style name="MyButton.Borderless" parent="android:Widget.Holo.Button.Borderless" />

面向对象的风格!它们看起来很漂亮,对吧?但看起来就是你所得到的 - 一种错觉,即当风格不是时风格是相关的。欺骗是MyButton.BorderlessMyButton有关,但它们没有任何共同之处!让我们通过删除名称中的点来消除混淆:

<style name="MyButton" parent="android:Widget.Holo.Button" />
<style name="MyBorderlessButton" parent="android:Widget.Holo.Button.Borderless" /> 

我在层次结构上看起来很漂亮,但我在代码中获得了很多实用工具。

样式与主题

样式和主题是两个不同的概念。虽然样式适用于单个View,但主题会应用于一组Views(或整个Activity)。

例如,假设您正在使用AppCompat,并且您想要为屏幕设置主要颜色。为此,您必须以整个Activity

为主题
<style name="MyTheme">
<style name="colorPrimary">@color/my_primary_color</style>
</style>

主题使用与样式相同的数据结构 - 即使使用style标记 - 但实际上它们在完全不同的情况下使用!他们不会使用相同的属性进行操作 - 例如,您可以在视图上定义textColor,但主题没有textColor属性。同样,主题中存在colorPrimary,但在样式中它们未被使用。因此:

规则#8:不要混合样式和主题。

我见过的两个常见错误:

  1. 将主题(作为样式)应用于“View”:        这没有任何意义,因为“View”无论如何都不能使用任何主题属性。没有任何事情发生。
  2. 通过育儿结合您的层次结构中的主题/样式。我之所以看到这是因为人们试图使用点符号来维持层次结构的错觉:                Stupid! So, stupid!它没有任何感觉,有时会以奇怪的方式失火。就是不要这样做!
  3. 从Lollipop开始,您可以将主题应用于View及其所有子项 2 。即使在这种情况下,你也不应该混淆两者,尽管你可以同时使用它们:

    <View 
    style="@style/MyView"
    android:theme="@style/MyTheme" />
    

    AppCompat对View有一个Toolbar主题的模拟,但是你会得到一段时间,直到Lollipop成为你应用的最低支持版本。换句话说 - 您可以在几年内享受此功能。 :P

    结论

    使用样式时,这些规则的统一元素是小心考虑周全。他们可以节省您的时间,但前提是您知道何时使用它们。

    字体:this article