一个FlowLayout带你学会自定义ViewGroup

时间过得真快,又到了写博客的时候了(/▽╲)。这次按照计划记录一个简单的自定义ViewGroup:流布局FlowLayout的实现过程,将自定义控件知识储备-View的绘制流程自定义控件知识储备-LayoutParams的那些事里的知识点结合起来,付诸实践。

1. 前言

早在学习Java的Swing基础知识的时候,就见到过里面的流布局FlowLayout,基本的效果就是让加入此容器的控件自左往右依次排列,如果当前行的宽度不足以容纳下一个控件,就会将此控件放置到下一行。其实这也跟css里向左浮动的效果很相似。

在Android的世界里,系统是没有提供类似FlowLayout布局的容器的。当然了,现在官方给我们提供了更强大也更复杂的FlexLayout了。不过嘛,本篇博客是总结一个自定义ViewGroup的实现流程,所以需要找一个难易适中的实例来进行分析,也就是FlowLayout了。(是的,我就是挑软柿子捏︿( ̄︶ ̄)︿)。

2. 效果

闲话少说,还是先来看看蘑菇君写的FlowLayout的功能:

  • 支持最基本的从左至右的排序,空间不足则换行
  • 支持设置子控件间的水平和竖直的间隔(也可以通过给每个child设置margin来实现,不过没有统一设置来的方便)
  • 支持绘制行之间的分割线
  • 支持FlowLayout本身的Gravity和child views的Gravity
  • 处理好FlowLayout的padding和child views的margin

这些都是FlowLayout基本的功能,效果如下图所示:

FlowLayout效果展示

是不是感觉还行?至少一般的情况下是能满足大部分人的需求滴。o( ̄▽ ̄)d

3. 分析

列举一下自定义ViewGroup的流程:

  1. 自定义属性:如果ViewGroup需要用到自定义属性,则需要声明、设置、解析并获取自定义属性值。
  2. 测量:在onMeasure方法里处理AT_MOSTEXACTLY两种测量模式下ViewGroup的宽高和children的宽高。(UNSPECIFIED模式可以暂不考虑)
  3. 布局:在onLayout方法里确定children的位置。
  4. 绘制:如果ViewGroup里需要绘制,则重写onDraw方法,按逻辑绘制。比如FlowLayout可以在每一行之间绘制一条分隔线。
  5. 处理LayoutParams:如果要为children定义布局属性,如layout_gravity,则需要自定义LayoutParams,并且重写ViewGroup相关的方法。
  6. 处理滑动事件:在本FlowLayout里暂时用不上…( ╯▽╰)

上面的步骤可能有所遗漏,不过也差不多啦。下面蘑菇君要根据上述的流程来一步一步的分析FlowLayout的源码,源码可能有点长,有些细节上的逻辑看不懂也莫方,只要了解流程对应的实现方式和注意事项就好,有兴趣的话可以稍后自己下载源码分析具体的逻辑实现。

好滴,那就让我们来一步一步的看,这个FlowLayout是如何在我手里…被玩残的…

3.1 自定义属性

3.1.1 声明属性

首先,自定义属性的第一步当然是声明属性,而最常使用的方式当然是在xml资源文件里(一般来说就是attrs.xml文件)声明需要使用的属性:

   <declare-styleable name="FlowLayout">
        <attr name="android:gravity"/>
        <attr name="horizonSpacing" format="dimension|reference"/>
        <attr name="verticalSpacing" format="dimension|reference"/>
        <attr name="dividerColor" format="color|reference"/>
        <attr name="dividerWidth" format="dimension|reference"/>
    </declare-styleable>

    <declare-styleable name="FlowLayout_Layout">
        <attr name="android:layout_gravity"/>
    </declare-styleable>

这里需要注意两个地方:

  1. 我们声明了两个declare-styleable,一个是为FlowLayout自身设置自定义属性;另一个是为孩子们提供额外属性,需要在自定义的LayoutParams里解析获取属性值。

  2. 大家都知道,我们在xml布局文件里使用自定义属性时,需要引入命名空间

xmlns:app="http://schemas.android.com/apk/res-auto"

使用自定义属性时,需要加上前缀app(或者是其它命名,只要一一对应)。但是有时候啊,我们自定义的属性名已经在系统中存在了,而且语义与我们想要的也很符合,比如如andrioid:textandroid:gravity等等。这个时候估计谁都会有一种“拿来主义”的冲动:直接使用系统里已经存在的属性名就好了嘛,多“原生”!既然有这种“邪恶”的需求,那Google工程师自然是要满足滴(~ ̄▽ ̄)~。

gravity属性为例,我们只要在declare-styleable里直接写上<attr name="android:gravity"/>即可,不过这里要注意的是不需要也不能再加上format属性,加上format属性就代表着这是在声明一个新的属性,不加则代表这是在使用已存在的一个属性。

3.1.2 使用属性

使用属性就比较简单了:

<wang.mogujun.widget.FlowLayout
        android:id="@+id/flow2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:background="#6A6A6A"
        android:gravity="start"
        android:padding="8dp"
        app:horizonSpacing="8dp"
        app:verticalSpacing="12dp"
        app:dividerColor="#cccccc"
        app:dividerWidth="2dp"
        >
3.1.3 解析并获取属性

在xml设置了相应的属性后,就需要在FlowLayout里解析并获取属性值了:


public static final int DEFAULT_SPACING = 8;
    public static final int DEFAULT_DIVIDER_COLOR = Color.parseColor(
  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是Java编写一个窗口的计算器的示例代码: ```java import java.awt.*; import java.awt.event.*;import javax.swing.*; public class Calculator extends JFrame implements ActionListener { private JTextField firstNumField, secondNumField, resultField; private JButton addButton, subtractButton, multiplyButton, divideButton; public Calculator() { super("计算器"); setLayout(new FlowLayout()); // 添加第一个文本框 firstNumField = new JTextField(10); add(firstNumField); // 添加加号按钮 addButton = new JButton("+"); addButton.addActionListener(this); add(addButton); // 添加减号按钮 subtractButton = new JButton("-"); subtractButton.addActionListener(this); add(subtractButton); // 添加乘号按钮 multiplyButton = new JButton("*"); multiplyButton.addActionListener(this); add(multiplyButton); // 添加除号按钮 divideButton = new JButton("/"); divideButton.addActionListener(this); add(divideButton); // 添加第二个文本框 secondNumField = new JTextField(10); add(secondNumField); // 添加等号按钮 JButton equalsButton = new JButton("="); equalsButton.addActionListener(this); add(equalsButton); // 添加结果文本框 resultField = new JTextField(10); resultField.setEditable(false); add(resultField); setSize(200, 200); setVisible(true); } public void actionPerformed(ActionEvent e) { double firstNum = Double.parseDouble(firstNumField.getText()); double secondNum = Double.parseDouble(secondNumField.getText()); double result = 0; if (e.getSource() == addButton) { result = firstNum + secondNum; } else if (e.getSource() == subtractButton) { result = firstNum - secondNum; } else if (e.getSource() == multiplyButton) { result = firstNum * secondNum; } else if (e.getSource() == divideButton) { result = firstNum / secondNum; } resultField.setText(Double.toString(result)); } public static void main(String[] args) { Calculator calculator = new Calculator(); calculator.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值