Android开发之玩转FlexboxLayout布局

点击上方“Android编程精选”,选择“置顶公众号”

关键时刻,第一时间送达!

在这之前,我曾认真的研究过鸿洋大神的Android 自定义ViewGroup 实战篇 -> 实现FlowLayout,按照大神的思路写出了一个流式布局,所有的东西都是难者不会会者不难,当自己能自定义流式布局的时候就会觉得这东西原来很简单了。如果各位小伙伴也看过那篇文章的话,应该知道自定义流式布局还是非常麻烦的,不过Google今年开源了新的容器,就是这个FlexboxLayout,如果你玩过前端开发或者玩过RN,就会觉得这个FlexboxLayout真是简单,OK,那我们今天就来看看这个FlexboxLayout的使用吧!先来看看显示效果:

OK,我们来看看这个东东要怎么实现吧!

引入项目

使用之前当然是先引入了,Google在GitHub上开源了这个控件,地址如下:

在项目中引入方式如下:

在module的gradle文件中添加如下一行:

[java] view plain copy print?
compile 'com.google.android:flexbox:0.2.3'

版本号为本文发布时的最新版本号。引入之后就可以使用了。

基本用法

根据GitHub上给我们的Demo,可以看到FlexboxLayout在使用的过程中只需要用容器将我们的子控件包裹起来就行了,主布局文件如下:

[java] view plain copy print?
 <RelativeLayout
   xmlns:android="http://schemas.android.com/apk/res/android"      
   xmlns:app="http://schemas.android.com/apk/res-auto"      
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"      
   android:layout_height="match_parent"      
   tools:context="org.lenve.flexbox.MainActivity">

   <com.google.android.flexbox.FlexboxLayout
       android:layout_width="match_parent"          
       android:layout_height="wrap_content"          
       app:flexWrap="wrap">

       <TextView      
           android:layout_width="wrap_content"              
           android:layout_height="wrap_content"              
           android:layout_margin="8dp"              
           android:background="@drawable/tv_bg"              
           android:padding="8dp"              
           android:text="1.水陆草木之花"/>
         

        <TextView              
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="2.可爱者甚蕃"/>
   
        
        <TextView              
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="3.晋陶渊明独爱菊"/>
 
        
        <TextView              
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="4.自李唐来"/>

         
         <TextView              
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="5.世人甚爱牡丹"/>
 
         
         <TextView              
            android:layout_width="wrap_content"    
            android:layout_height="wrap_content"    
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="6.予独爱莲之出淤泥而不染"/>
            
        <TextView              
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="7.濯清涟而不妖"/>
            
        <TextView      
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="8.中通外直"/>


         <TextView              
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="9.不蔓不枝"/>
            
        <TextView  
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="10.香远益清"/>
            
        <TextView  
            android:layout_width="wrap_content"              
            android:layout_height="wrap_content"              
            android:layout_margin="8dp"              
            android:background="@drawable/tv_bg"              
            android:padding="8dp"              
            android:text="11.亭亭净植"/>
     com.google.android.flexbox.FlexboxLayout>
 
RelativeLayout>

显示效果就是我们上文贴出来的图。

父容器属性简介

flexWrap属性表示换行与否,默认为noWrap,表示不换行,wrap表示自动换行,还有一个wrap_reverse 表示副轴反转,副轴的含义我们一会来说。

flexDirection 表示子元素的排列方向,元素的排列方向为主轴的方向,该属性有四种取值,不同取值对应不同的主副轴,看下面一张图:

默认为row,所以如果我给flexWrap取wrap_reverse属性,则效果如下:

副轴取反,由于flexDirection默认为row,即副轴为竖直方向,副轴取反,即竖直方向倒着显示。同理,如果我给flexDirection属性设置为column,对应主轴方向为竖直向下,这个时候控件就会按如下方式来显示:

其它值我就不一一测试了。

justifyContent 表示控件沿主轴对齐方向,有五种取值,默认情况下大家看到控件是左对齐(flex_start),另外还有主轴居中对齐(center):

主轴居右对齐(flex_end):

两端对齐,子元素之间的间隔相等,但是两端的子元素分别和左右两边的间距为0(space_between):

子元素两端的距离相等,所有子元素两端的距离都相相等(space_around):

alignContent 表示控件在副轴上的对齐方向(针对多行元素),默认值为stretch,表示占满整个副轴,因为上文中我把FlexboxLayout的高度设置为包裹内容,所以这个属性大家可能没看到效果,这里我把FlexboxLayout的高度改为match_parent,我们再来看看效果:

代码:

[java] view plain copy print?  
<com.google.android.flexbox.FlexboxLayout
     android:layout_width="match_parent"        
     android:layout_height="match_parent"
      app:alignContent="stretch"        
      app:flexWrap="wrap">
 
....  
....

效果:

大家看到系统会自动放大子元素的高度以使之填满父容器。

与副轴起点对齐(flex_start):

代码:

[java] view plain copy print?    
<com.google.android.flexbox.FlexboxLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"    
       app:alignContent="flex_start"          
       app:flexWrap="wrap">
 
....  
....

与副轴终点对齐(flex_end):

[java] view plain copy print?    
<com.google.android.flexbox.FlexboxLayout
       android:layout_width="match_parent"  
       android:layout_height="match_parent"          
       app:alignContent="flex_end"          
       app:flexWrap="wrap">
 
...  
...

还有两个值,分别是space_around和space_between,意思和上文说的一样,这里不再赘述。

alignItems 也是描述元素在副轴上的对齐方向(针对单行),属性含义和上文基本一致,只是多了一个baseline,表示基线对齐,其余属性不赘述。

这里说的都是父容器的属性,那么子元素都有哪些属性呢?

子元素属性简介

[java] view plain copy print?
app:layout_order="2"

这个表示子元素的优先级,默认值为1,数值越大越靠后显示。

[java] view plain copy print?
app:layout_flexGrow="2"

这个类似于权重属性,来个示例代码:

[java] view plain copy print?
<com.google.android.flexbox.FlexboxLayout
   android:layout_width="300dp"
   android:layout_height="wrap_content">

   <TextView  
        android:layout_width="0dp"          
        android:layout_height="48dp"          
        android:background="@color/colorPrimary"          
        app:layout_flexGrow="2"/>
        
   <TextView    
       android:layout_width="0dp"          
       android:layout_height="48dp"          
       android:background="@color/colorAccent"          
       app:layout_flexGrow="1"/>
 
com.google.android.flexbox.FlexboxLayout>

显示效果:

[java] view plain copy print?
app:layout_flexShrink="2"

表示空间不足时子控件的缩放比例,0表示不缩放,比如下面一行代码:

[java] view plain copy print?
<com.google.android.flexbox.FlexboxLayout
   android:layout_width="300dp"
   android:layout_height="wrap_content">

   <TextView    
       android:layout_width="300dp"          
       android:layout_height="48dp"          
       app:layout_flexShrink="2"          
       android:background="@color/colorPrimary"/>
       
   <TextView    
       app:layout_flexShrink="1"          
       android:layout_width="100dp"          
       android:layout_height="48dp"          
       android:background="@color/colorAccent"/>
 
com.google.android.flexbox.FlexboxLayout>

父容器总宽度为300dp,结果两个子元素加起来就400,超过了100dp,总共需要缩小100dp,根据flexShrink属性,第一个TextView缩小100的三分之二,第二个TextView缩小100的三分之一。

Kivy,一个跨平台开发神器的 Python 库!

大家好,我是老马。今天要介绍一个超强大的GUI开发框架 —— Kivy!如果你想用Python开发手机App或者桌面应用,Kivy绝对是最佳选择之一。它不仅支持Windows、Mac、Linux等桌面平台,还能开发Android和iOS应用。最棒的是,它还支持多点触控!让我们一起来探索这个跨平台神器吧!

快速入门

首先安装Kivy:

pip install kivy

让我们创建一个最简单的Kivy应用:

from kivy.app import App
from kivy.uix.label import Label

class HelloApp(App):
    def build(self):
        return Label(text='Hello, Kivy!')

if __name__ == '__main__':
    HelloApp().run()

小贴士:Kivy使用了自己的图形引擎,所以性能非常好,而且跨平台兼容性极强!

基础控件1. 按钮和事件处理

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout

class ButtonDemo(App):
    def build(self):
        layout = BoxLayout(orientation='vertical')
        
        # 创建按钮并绑定事件
        btn1 = Button(text='点我试试!')
        btn1.bind(on_press=self.on_button_press)
        
        # 创建第二个按钮
        btn2 = Button(text='我也是按钮!')
        btn2.bind(on_press=lambda x: self.on_button_press(x, msg="第二个按钮"))
        
        # 添加按钮到布局
        layout.add_widget(btn1)
        layout.add_widget(btn2)
        
        return layout
    
    def on_button_press(self, instance, msg="第一个按钮"):
        print(f'{msg}被点击了!')

if __name__ == '__main__':
    ButtonDemo().run()

2. 文本输入和标签

from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout

class InputDemo(App):
    def build(self):
        layout = BoxLayout(orientation='vertical')
        
        # 创建文本输入
        self.input = TextInput(
            multiline=False,
            hint_text='请输入文字...',
            size_hint_y=None,
            height=40
        )
        self.input.bind(text=self.on_text_change)
        
        # 创建标签显示输入内容
        self.label = Label(text='输入的内容会显示在这里')
        
        # 添加到布局
        layout.add_widget(self.input)
        layout.add_widget(self.label)
        
        return layout
    
    def on_text_change(self, instance, value):
        self.label.text = f'你输入的是: {value}'

if __name__ == '__main__':
    InputDemo().run()

高级特性1. KV语言

Kivy提供了专门的KV语言来分离界面设计和逻辑代码:

# main.py
from kivy.app import App
from kivy.uix.widget import Widget

class Calculator(Widget):
    def calculate(self):
        try:
            self.display.text = str(eval(self.display.text))
        except Exception as e:
            self.display.text = 'Error'

class CalculatorApp(App):
    def build(self):
        return Calculator()

if __name__ == '__main__':
    CalculatorApp().run()

# calculator.kv
:
display: display

BoxLayout:
orientation: 'vertical'
TextInput:
id: display
multiline: False
size_hint_y: None
height: 50

GridLayout:
cols: 4
Button:
text: '7'
on_press: display.text += self.text
Button:
text: '8'
on_press: display.text += self.text
Button:
text: '9'
on_press: display.text += self.text
Button:
text: '/'
on_press: display.text += self.text
Button:
text: '4'
on_press: display.text += self.text
Button:
text: '5'
on_press: display.text += self.text
Button:
text: '6'
on_press: display.text += self.text
Button:
text: '*'
on_press: display.text += self.text
Button:
text: '1'
on_press: display.text += self.text
Button:
text: '2'
on_press: display.text += self.text
Button:
text: '3'
on_press: display.text += self.text
Button:
text: '-'
on_press: display.text += self.text
Button:
text: '0'
on_press: display.text += self.text
Button:
text: '.'
on_press: display.text += self.text
Button:
text: '='
on_press: root.calculate()
Button:
text: '+'
on_press: display.text += self.text

2. 动画效果

Kivy支持丰富的动画效果:

from kivy.app import App
from kivy.uix.button import Button
from kivy.animation import Animation
from kivy.uix.floatlayout import FloatLayout

class AnimationDemo(App):
    def build(self):
        layout = FloatLayout()
        
        self.button = Button(
            text='点我动起来!',
            size_hint=(NoneNone),
            size=(10050),
            pos_hint={'center_x'.5'center_y'.5}
        )
        self.button.bind(on_press=self.animate_it)
        
        layout.add_widget(self.button)
        return layout
    
    def animate_it(self, instance):
        # 创建动画序列
        anim = Animation(pos_hint={'center_x'.2}, duration=1)
        anim += Animation(pos_hint={'center_y'.7}, duration=1)
        anim += Animation(size=(200100), duration=.5)
        anim += Animation(size=(10050), duration=.5)
        anim += Animation(pos_hint={'center_x'.5'center_y'.5}, duration=1)
        
        # 开始动画
        anim.start(instance)

if __name__ == '__main__':
    AnimationDemo().run()

实战示例:todo清单应用

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.listview import ListView
from kivy.uix.listitem import ListItemButton
from kivy.adapters.listadapter import ListAdapter

class TodoApp(App):
    def build(self):
        # 主布局
        layout = BoxLayout(orientation='vertical')
        
        # 输入区域
        input_layout = BoxLayout(
            size_hint_y=None,
            height=50
        )
        self.task_input = TextInput(
            multiline=False,
            hint_text='输入新任务...'
        )
        add_button = Button(
            text='添加',
            size_hint_x=None,
            width=100
        )
        add_button.bind(on_press=self.add_task)
        
        input_layout.add_widget(self.task_input)
        input_layout.add_widget(add_button)
        
        # 任务列表
        self.tasks = []
        self.list_adapter = ListAdapter(
            data=self.tasks,
            cls=ListItemButton,
            selection_mode='single',
            allow_empty_selection=True
        )
        self.task_list = ListView(adapter=self.list_adapter)
        
        # 删除按钮
        delete_button = Button(
            text='删除选中',
            size_hint_y=None,
            height=50
        )
        delete_button.bind(on_press=self.delete_task)
        
        # 添加所有组件到主布局
        layout.add_widget(input_layout)
        layout.add_widget(self.task_list)
        layout.add_widget(delete_button)
        
        return layout
    
    def add_task(self, instance):
        task = self.task_input.text.strip()
        if task:
            self.tasks.append(task)
            self.list_adapter.data = self.tasks
            self.task_input.text = ''
    
    def delete_task(self, instance):
        if self.task_list.adapter.selection:
            selected = self.task_list.adapter.selection[0]
            self.tasks.remove(selected.text)
            self.list_adapter.data = self.tasks

if __name__ == '__main__':
    TodoApp().run()

练习题

制作一个简单的画板应用:

开发一个音乐 器:

总结

Kivy是一个强大的跨平台GUI框架,它的主要优点包括:

使用Kivy的建议:

我建议大家从简单的界面开始练习,逐步尝试更复杂的交互和动画。记住,选择Kivy就是选择了跨平台的自由!

下期我会给大家介绍如何用Kivy开发一个完整的手机应用,并上架到应用商店,敬请期待!

本站内容来自用户投稿,如果侵犯了您的权利,请与我们联系删除。联系邮箱:835971066@qq.com

本文链接:http://news.xiuzhanwang.com/post/2747.html

发表评论

评论列表

还没有评论,快来说点什么吧~

友情链接: