主题,样式和其他


几乎所有开发人员都知道android中有主题,但是他们的使用通常仅限于从Stack Overflow或其他资源复制xml。 Internet上有关于主题的信息,但这通常只是如何获得特定结果的秘诀。 在本文中,我试图对android风格化机制进行介绍。

目录内容


引言
RectView的属性
RectView的样式
默认样式
RectView样式主题属性
活动级别主题
查看主题
如何应用属性值
资源主题
总结

引言


android中的样式和主题是一种机制,可让您将设计细节(例如颜色,字体大小等)与UI的结构分开。 要了解其工作原理,一个带有自定义视图的简单示例将对我们有所帮助。

我们将需要一个简单的自定义视图,该视图将在视图范围内绘制具有所需颜色的矩形。

class RectView @JvmOverloads constructor( context: Context, attrSet: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrSet, defStyleAttr) { private val paint = Paint().apply { color = Color.BLUE } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) } } 



RectView的属性


现在,我希望能够更改布局矩形的颜色。
为此,我们需要添加一个新属性,将attrs.xml添加到文件中

 <resources> <attr name="rectColor" format="color"/> </resources> 

现在在代码中,我们可以通过R.attr.rectColor和
在屏幕的布局中,我们可以使用app:rectColor属性。

 <org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" app:rectColor="@color/colorPrimary" android:layout_height="100dp"/> 

但是RectView尚不知道有一个属性可以为矩形取颜色。

让我们教RectView了解rectColor属性,添加一组属性

 <resources> <attr name="rectColor" format="color"/> <declare-styleable name="RectView"> <attr name="rectColor"/> </declare-styleable> </resources> 

在类R中,生成了2个新字段:
R.styleable.RectView是id属性的数组,当前它是一个元素的数组R.attr.rectColor
R.styleable.RectView_rectColor是R.styleable.RectView数组中属性的ID索引,即 我们可以获得的id属性,因此R.styleable.RectView [R.styleable.RectView_rectColor]

并将对rectColor属性的支持添加到代码中。

 init { val typedArray = context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, 0 ) try { paint.color = typedArray.getColor( R.styleable.RectView_rectColor, Color.BLUE ) } finally { typedArray.recycle() } } 

完整代码
 class RectView @JvmOverloads constructor( context: Context, attrSet: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrSet, defStyleAttr) { private val paint = Paint().apply { color = Color.BLUE } init { val typedArray = context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, 0 ) try { paint.color = typedArray.getColor( R.styleable.RectView_rectColor, Color.BLUE ) } finally { typedArray.recycle() } } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint) } } 


现在我们可以从布局中更改矩形的颜色:



RectView的样式


现在我们只有一个属性,但假设有十个属性-每次在布局中为相似的元素设置所有这些属性将非常不便。

使一切都变得时尚的决定。

 <style name="DefaultRectViewStyle"> <item name="rectColor">@color/colorAccent</item> </style> 

现在,我们可以通过指定样式来创建具有相同外观的任何类型的视图。

 <org.berendeev.themes.RectView android:id="@+id/text2" android:layout_width="100dp" style="@style/DefaultRectViewStyle" android:layout_height="100dp"/> 

默认样式


现在,我们希望所有视图默认都具有某种样式。 为此,我们将为getsStyledAttributes方法指定默认样式:

 context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, 0, R.style.DefaultRectViewStyle ) 

够了 现在,我们添加到布局中的所有RectView都将具有默认样式DefaultRectViewStyle。

RectView样式主题属性


默认值为good,但是我想更灵活地控制布局。 为整个应用程序或单独的Activity设置特定视图的样式很方便。

为此,我们需要一个新属性。 我们将在主题中设置其值,该值将是确定RectView外观的样式。

 <attr name="rectViewStyle" format="reference"/> 

并教我们的rectView了解此主题属性。 通过将第三个参数R.attr.rectViewStyle传递给gainStyledAttributes方法。

 context.theme.obtainStyledAttributes( attrSet, R.styleable.RectView, R.attr.rectViewStyle, R.style.DefaultRectViewStyle ) 

现在,如果在主题中指定了名称为rectViewStyle且类型为style的值的项目,则此样式将应用于具有该主题的所有RectView。

活动级别主题


我们在主题中设置rectViewStyle属性的值。

 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style> </resources> 

在应用程序清单中指定主题。

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.berendeev.themes"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> 

所有活动将设置为默认的AppTheme主题。 我们为整个应用程序设置了RectView设计。

也可以为清单中的特定活动设置主题。

 <activity android:name=".MainActivity" android:theme="@style/MyTheme"> 

在此重要的是,我们将在应用程序或活动级别设置的主题应继承自标准主题。 例如,Theme.AppCompat。 否则,我们会在运行时崩溃。
java.lang.IllegalStateException:您需要在此活动中使用Theme.AppCompat主题(或后代)。

查看主题


在视图级别,我们无需重新安装主题,但会覆盖所需的属性值,因此我们无需继承标准主题。 父级可以为空,例如,我们可以从ThemeOverlay.AppCompat叠加层之一继承。

在这里,我们为LinearLayout组及其层次结构中的所有后代设置MyOverlay主题。

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:theme="@style/MyOverlay"> <org.berendeev.themes.RectView android:layout_width="100dp" android:layout_height="100dp"/> </LinearLayout> 

在这个主题上,我们可以没有父母,因为 我们只是修改活动主题。

 <style name="MyOverlay"> <item name="rectViewStyle">@style/LocalRectViewStyle</item> </style> 

如何应用属性值



如果对于视图,我们同时在布局,样式和主题中定义了属性值,那么选择值的顺序将如下。 最高优先级是布局中指定的值,即 如果在布局中设置了该值,则样式和主题将被忽略,然后样式继续,然后是主题,最后是默认样式。

资源主题


主题值可以在资源中使用。 例如,在drawable中,我们可以设置一种颜色,该颜色将取决于主题中设置的颜色。

 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <solid android:color="?attr/colorPrimary"/> </shape> 

现在,矩形的颜色取决于主题中colorPrimary属性的值。 该属性可以是任何。 例如,我们可以在资源中设置属性,并在主题中设置其值。

这样的技巧可以在所有资源中使用,例如在选择器或矢量绘图中。 这使设计更加结构化和灵活。 我们可以快速更改所有活动的主题。

总结


  • 资源级别的主题和样式是相同的,但是使用方式有所不同。
  • 主题可以包含其他主题,只是视图的含义或样式。
  • 样式在视图级别的布局中指示。 LayoutInflater计算样式值,并将其作为AttributeSet传递给View构造函数。
  • 主题是一种机制,使您可以全局定义整个活动的外观。
  • 可以为视图层次结构中的元素(及其后代)更改主题值。
  • 主题值不仅可以在View中使用,而且可以在资源中使用。

PS:如果这篇文章有趣,我将写一篇更高级的文章或包含许多实际示例的文章。

Source: https://habr.com/ru/post/zh-CN453812/


All Articles