当前位置:首页 > 安卓源码 > 技术博客 >

使用Java和XML为Android创建自己的控件

时间:2018-08-08 23:24 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

介绍 在Android中,我们有很多方法将布局分组在一起,使它们可以重复使用。 在我们的XML设计中 有一个 include Tag(以及鲜为人知的 merge ),它只包含当前版本中的另一个布局 Fragments ,当然,我们可以随时从任何基类派生出来,比如 View 或 LinearLayou

介绍

在Android中,我们有很多方法将布局“分组”在一起,使它们可以重复使用。在我们的XML设计中有一个<include>Tag(以及鲜为人知的<merge>),它只包含当前版本中的另一个布局Fragments,当然,我们可以随时从任何基类派生出来,比如ViewLinearLayout

本文将深入探讨后者:

  • 如何编写一个扩展自己布局的控件?
  • 如何为我的控件创建自定义属性?
  • 这些都与风格和主题混合在一起怎么样?

本文的范围是扩展自己的布局的控件,因此我们将从匹配的基类(a LinearLayout或a)派生RelativeLayout,具体取决于我们的自定义布局的构建方式。

我将在这里使用我自己的一个控件作为示例,向您展示它是如何完成的。这个控件是一个简单的工具栏,有四个按钮,支持我的软件标签的一些标准功能,比如给我发电子邮件,打开我的G +页面,在Google Play上打开我的开发者页面以及评价当前正在运行的应用程序。

它非常简单,因此在文章中进行分析是一个很好的选择。

在运行时,我的控件如下所示:

使用Java和XML为Android创建自己的控件

这是从我的一个使用黑暗主题的应用程序的屏幕截图中获取的。

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设置的

第1步:创建您的控件类

最好的事情是:创建你的类并决定你的基类是什么。在我们的例子中,这很简单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的指定属性,包括我们的自定义属性 barSizeshowTitle所以这已经揭开了第一个谜团:我们如何从XML获取值到我们的控制?答案是:通过AttributeSet当我们讨论该init(...)方法时,我们如何从中获取我们的价值

将xml-enum值映射到代码值

@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中的这些值,我们不想使用01

那么这是什么@MbarBarSize以及它做了什么?@annotation讲述这个int变量只能存放在定义的值@MbarBarSize如果您尝试分配其他内容,则会收到lint警告。

要为成员宣布此类限制,会发布@interface声明。MbarBarSize定义为:

@Retention(RetentionPolicy.CLASS)
public @interface MbarBarSize {
   int SMALL = 0;
   int LARGE = 1;
}

通过这样的声明,您可以分配我称之为value-restrictionvalue-constraint通常允许其他值的数据类型。

的RetentionPolicy

了解何时使用哪个策略非常重要。有三种政策可供选择:

  • CLASS :(默认值)。这个策略可以在任何地方使用,但我主要在库中使用它。Class意味着,这@interface将在编译后继续存在,并且仍可供您的库用户使用。他们可以使用这些值SMALLLARGE就像他们在自己的app / lib中定义它们一样。这就是你想要的,如果你需要它作为方法参数和你想要的话,lint可以警告lib的用户,如果他们分配了一个不允许的值。库中,您希望您的值与其给定名称一起使用。您不希望强制库的用户将“ 0”和“ 1”作为参数值提供给您的方法。他们也应该使用SMALLLARGE
  • SOURCE:此策略告诉编译器放弃该定义。对于人类思维,这意味着:当您定义@interface不需要在编译过程中存活时,可以使用SOURCE策略举个例子:在你的应用程序,没事的时候你的应用程序之外,需要与他们的指定名称(以访问值SMALLLARGE在上面的例子)。外当前项目的,SMALLLARGE未知的用户必须提供“ 0”和“ 1”作为参数值。这不是您想要的public接口和方法,但您可以将它用于private库中的内容。
  • RUNTIME:这是所有人中最宽泛的政策。它不仅可以在编译过程中存在并且可供您的用户使用,甚至可以在程序运行时使用,并且可以通过反射访问!除此之外,它的行为类似于CLASS

第2步:定义自定义属性

好的,我们已经看到,我们如何将属性值映射到我们的Java代码,但该属性是如何定义的

您可以通过将名为attrs.xml的文件添加项目的values文件夹创建自定义属性右键单击values项目资源管理器中节点,然后选择“ 新建” - >“ 值资源文件”将文件命名为attrs.xml

使用Java和XML为Android创建自己的控件

在此文件中,您可以创建自定义属性。语法并不令人惊讶且易于理解。我在这里向您展示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”在这里对应于01我们的整数表示的@interface控件的用户可以在XML布局中将值指定为“ small”和“ large”。
  • 定义为format="boolean"我们添加一个简单的开关来显示/隐藏标题文本“ More mbar Software”以进一步定制到控件中。

第3步:将各个部分组合在一起:init(...)方法

因此,现在您已经定义了自定义属性,您已经了解了它在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=SMALLshowTitle=true(参见本文中的第一个代码块 - 构造函数,这是类成员的设置方式)。

这里最重要的部分是从XML获取属性。这是通过该方法完成的,该方法obtainStyledAttributes与我们当前的主题相关联context正如我们所得到的LinearLayout,我们有一个getContext()可用方法,所以我们不需要参数或其他方法来获得有效的上下文。我们已经有一个。

参数是:

  • attrs这是AttributeSet我们想要获得价值的地方。它是构造函数中提供的那个。
  • R.styleable.MbarBar我相信,现在你知道那是什么了。我们在attrs.xml中定义的自定义属性我们希望得到这些价值。
  • defStyleAttrRes一切都是主题和风格。Android将应用当前主题和样式的任何修改,并考虑我们将获得的值。

这个电话后,我们可以为我们将访问同样简单的方式访问我们的属性ExtrasBundle随着getIntgetBoolgetWhatYouNeed非常简单的界面。这些调用中的第二个参数是default-if-not-found。

接下来,有一个switch 声明,膨胀两个预定义布局之一(小和大按钮框架)。这些布局没有什么特别之处,它们也被设计为任何其他布局。只是一堆图像和文本视图。标准。您可以像充气的任何其他布局一样给它充气。

然后调用一些其他支持方法,如隐藏标题栏和连接单击侦听器,但它们不是本文的范围。我们想用自定义属性创建自定义控件:)。

很酷的事:这在设计时已经可见了!如果您打开了预览窗口,则在布局时看到设计已膨胀!

如果您更改XML设计器中的任何自定义属性(例如设置“ showTitle”到“ false”),它会立即反映在布局中,如您所料。

使用Java和XML为Android创建自己的控件

我们创造了什么

  • 我们定义了一个派生自的新控件类 LinearLayout
  • 我们在可设置样式的资源中创建了两个自定义属性
  • 我们创建了一个在同一代码中@interface反映自定义属性enum值的方法
  • 我们已经通过Java访问了Java代码中的自定义属性 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

技术博客阅读排行

最新文章