在Android中,我们有很多方法将布局“分组”在一起,使它们可以重复使用。在我们的XML设计中有一个<include>
Tag(以及鲜为人知的<merge>
),它只包含当前版本中的另一个布局Fragments
,当然,我们可以随时从任何基类派生出来,比如View
或LinearLayout
。
本文将深入探讨后者:
本文的范围是扩展自己的布局的控件,因此我们将从匹配的基类(a LinearLayout
或a)派生RelativeLayout
,具体取决于我们的自定义布局的构建方式。
我将在这里使用我自己的一个控件作为示例,向您展示它是如何完成的。这个控件是一个简单的工具栏,有四个按钮,支持我的软件标签的一些标准功能,比如给我发电子邮件,打开我的G +页面,在Google Play上打开我的开发者页面以及评价当前正在运行的应用程序。
它非常简单,因此在文章中进行分析是一个很好的选择。
在运行时,我的控件如下所示:
这是从我的一个使用黑暗主题的应用程序的屏幕截图中获取的。
XML设计中的声明:
<mbar.ui.controls.MbarBar
android:id="@+id/contact_mbar_button_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_mbar_credit_text"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/mbar_default_control_distance"
mbar:barSize="small"/>
可能有不同的方法来实现这一目标:
<include>
从库中预先绘制的布局,并在代码中分配按钮侦听器
当然,我们将采用本文中的第三种方法。您可以在此XML代码段中看到的是控件至少使用一个自定义属性:barSize
。它是一个enum
类型属性,知道值“ small
”(0)和“ large
”(1)。我们将很快创建它,以及第二个自定义属性showTitle
,一个简单的布尔值。
我们假设您的库/项目是使用minAPI 17设置的。
最好的事情是:创建你的类并决定你的基类是什么。在我们的例子中,这很简单LinearLayout
。
有几个构造函数可用,并不是所有构造函数都需要提供,但我总是试图尽可能完全覆盖基类。
所以,当你开始上课时extends LinearLayout
,有4个构造函数可用:
public class MbarBar extends LinearLayout {
private @MbarBarSize int barSize = MbarBarSize.SMALL;
private boolean showTitle = true;
// <editor-fold desc="Constructors">
public MbarBar(Context context) {
super(context);
init(null, 0, 0);
}
public MbarBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
public MbarBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr, 0);
}
@TargetApi(21)
public MbarBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr, defStyleRes);
}
// </editor-fold>
你可以看到,我将它们全部重定向到一个init(...)
方法。我们将稍后介绍一下,让我们现在专注于构造函数。
@TargetApi(21)
第4 个构造函数周围有一个注释,因为这个注释只能在21+以上。
系统提供给构造函数的值是多少?
第一条规则:将它们传递给超级班级,除非你有充分的理由,不要这样做!
第二:对于我们的开发人员,第二个参数AttributeSet
是最感兴趣的,因为这个参数包含来自XML的指定属性,包括我们的自定义属性 barSize
和showTitle
。所以这已经揭开了第一个谜团:我们如何从XML获取值到我们的控制?答案是:通过AttributeSet
。当我们讨论该init(...)
方法时,我们如何从中获取我们的价值。
我@interfaces
非常喜欢那些,所以我创造了一个barSize
。如果您不知道它是什么:它或多或少是将整数或字符串组合在一起的另一种方法。Android SDK在很多方面做到了这一点,而且你遇到过它们。就像一个简单的例子:每当你设置某些东西时,View.VISIBLE
或者View.GONE
你正在接触其中一个。
在课程的顶部,您会看到声明:
private @MbarBarSize int barSize = MbarBarSize.SMALL;
这与XML设计中的这一行相关:
mbar:barSize="small"
稍后我们讨论这个自定义属性的声明时,您会看到术语“ small
”和“ large
”代表值“ 0
”和“ 1
”。现在,我们当然希望用相同的名称(SMALL和LARGE
)处理Java中的这些值,我们不想使用0
和1
。
那么这是什么@MbarBarSize
以及它做了什么?在@annotation
讲述这个int
变量只能存放在定义的值@MbarBarSize
。如果您尝试分配其他内容,则会收到lint警告。
要为成员宣布此类限制,会发布@interface
声明。的MbarBarSize
定义为:
@Retention(RetentionPolicy.CLASS)
public @interface MbarBarSize {
int SMALL = 0;
int LARGE = 1;
}
通过这样的声明,您可以分配我称之为value-restriction
或value-constraint
通常允许其他值的数据类型。
了解何时使用哪个策略非常重要。有三种政策可供选择:
Class
意味着,这@interface
将在编译后继续存在,并且仍可供您的库用户使用。他们可以使用这些值SMALL
,LARGE
就像他们在自己的app / lib中定义它们一样。这就是你想要的,如果你需要它作为方法参数和你想要的话,lint可以警告lib的用户,如果他们分配了一个不允许的值。在库中,您希望您的值与其给定名称一起使用。您不希望强制库的用户将“ 0
”和“ 1
”作为参数值提供给您的方法。他们也应该使用SMALL
和LARGE
!@interface
不需要在编译过程中存活时,可以使用SOURCE策略。举个例子:在你的应用程序,没事的时候你的应用程序之外,需要与他们的指定名称(以访问值SMALL
和LARGE
在上面的例子)。外当前项目的,SMALL
并LARGE
是未知的。用户必须提供“ 0
”和“ 1
”作为参数值。这不是您想要的public
接口和方法,但您可以将它用于private
库中的内容。好的,我们已经看到,我们如何将属性值映射到我们的Java代码,但该属性是如何定义的?
您可以通过将名为attrs.xml的文件添加到项目的values文件夹来创建自定义属性。右键单击values
项目资源管理器中的节点,然后选择“ 新建” - >“ 值资源文件”。将文件命名为attrs.xml。
在此文件中,您可以创建自定义属性。语法并不令人惊讶且易于理解。我在这里向您展示MbarBar
控件的完整属性集:
<declare-styleable name="MbarBar">
<attr name="barSize" format="enum">
<enum name="small" value="0"/>
<enum name="large" value="1"/>
</attr>
<attr name="showTitle" format="boolean"/>
</declare-styleable>
您styleable
在此处声明了一个资源,这将使XML设计器可以使用它。
有几种格式可供选择,这篇文章的范围太远,无法在这里覆盖它们,但enum
格式是最有趣的格式之一,我们将看一下:
<declare-styleable name="class_name_of_your_control">
你可能不会name
在这里自由选择“ ”属性!这已经是你的控件类的连接(以及我们在第一步创建类的原因以及之后的属性)!我们的控件被命名MbarBar
,这是这个确切的控件/类名。使用此单行,您在此样式中声明的所有内容都将附加到MbarBar
该类。然后,声明了两个自定义属性:
format="enum"
,然后我们可以添加<enum value
我们喜欢的任意数量的列表。我们给他们一个名字和一个价值。你看,“ 0
”和“ 1
”在这里对应于0
和1
我们的整数表示的@interface
。控件的用户可以在XML布局中将值指定为“ small
”和“ large
”。format="boolean"
我们添加一个简单的开关来显示/隐藏标题文本“ More mbar Software
”以进一步定制到控件中。
因此,现在您已经定义了自定义属性,您已经了解了它在XML布局中的外观,让我们将它们放在一起并查看AttributeSet
以获取开发人员在XML中输入的值。
方法首先,后来说明:
private void init(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
if (attrs != null) {
TypedArray array = getContext().getTheme().obtainStyledAttributes
(attrs, R.styleable.MbarBar, defStyleAttr, defStyleRes);
barSize = array.getInt(R.styleable.MbarBar_barSize, 0);
showTitle = array.getBoolean(R.styleable.MbarBar_showTitle, true);
}
switch (barSize) {
case MbarBarSize.SMALL:
View smallMe = inflate(getContext(), R.layout.mbar_button_frame_small, this);
break;
case MbarBarSize.LARGE:
View largeMe = inflate(getContext(), R.layout.mbar_button_frame, this);
break;
}
setTitleBarVisibility();
connectListeners();
}
其中一个构造函数(第一个)将null
作为attrs值发送给此方法,因此我们需要null
-check。在这种情况下,控件将在默认情况下使用所有值:barSize=SMALL
和showTitle=true
(参见本文中的第一个代码块 - 构造函数,这是类成员的设置方式)。
这里最重要的部分是从XML获取属性。这是通过该方法完成的,该方法obtainStyledAttributes
与我们当前的主题相关联context
。正如我们所得到的LinearLayout
,我们有一个getContext()
可用的方法,所以我们不需要参数或其他方法来获得有效的上下文。我们已经有一个。
参数是:
attrs
。这是AttributeSet
我们想要获得价值的地方。它是构造函数中提供的那个。R.styleable.MbarBar
。我相信,现在你知道那是什么了。我们在attrs.xml中定义的自定义属性。我们希望得到这些价值。defStyleAttr
和Res
。一切都是主题和风格。Android将应用当前主题和样式的任何修改,并考虑我们将获得的值。
这个电话后,我们可以为我们将访问同样简单的方式访问我们的属性Extras
的Bundle
。随着getInt
,getBool
,getWhatYouNeed
。非常简单的界面。这些调用中的第二个参数是default-if-not-found。
接下来,有一个switch
声明,膨胀两个预定义布局之一(小和大按钮框架)。这些布局没有什么特别之处,它们也被设计为任何其他布局。只是一堆图像和文本视图。标准。您可以像充气的任何其他布局一样给它充气。
然后调用一些其他支持方法,如隐藏标题栏和连接单击侦听器,但它们不是本文的范围。我们想用自定义属性创建自定义控件:)。
很酷的事:这在设计时已经可见了!如果您打开了预览窗口,则在布局时看到设计已膨胀!
如果您更改XML设计器中的任何自定义属性(例如设置“ showTitle
”到“ false
”),它会立即反映在布局中,如您所料。
LinearLayout
@interface
反映自定义属性enum
值的方法AttributeSet
当您第一次使用自定义控件时,不要感到困惑,在键入自定义属性的名称时,您在XML中没有智能感知!
您需要知道在android:
命名空间中找不到属性,也不在app:
命名空间中找不到属性。您将为此添加您喜欢的任何命名空间名称(如果是此控件,您可以在我使用的顶部XML中看到mbar:
)。
当您开始键入新的命名空间名称时,Android Studio会让您插入...
xmlns:mbar="http://schemas.android.com/apk/res-auto"
...使用Alt + Enter。接受(=按Alt + Enter)。然后您的自定义属性可用此前缀。
所以...我们在这里!
我希望这篇文章可以帮助您完成自定义控件的第一步,并使该部分失去神秘感。
欢迎评论一如既往,我会尽力回答任何问题!
使用Java和XML为Android创建自己的控件 转载https://www.codesocang.com/appboke/38809.html
热门源码