利用 RN的渲染机制在IOS实现右下角浮动按钮



  • ===========2016-03-28 更新============

    经过进一步测试,@sunnylqm 所说的使用top来定位是正确的。不需要使用flex来为floatingbutton在屏幕上分一块出来也照样能够根据top的值定位于屏幕的固定位置。

    但是需要注意的是,根据top值,确实能够固定在特定位置,但是想要实现其悬浮在其他组件上面的效果,则必须使先要悬浮的组件必须位于其他组件之后,才能有悬浮的效果,否则其他组件就会悬浮于其上。

    <View>
       <其他组件/>
       <想要悬浮的组件/>
    </View>
    

    ===========分割线================

    众所周知,在IOS里面没有提供像安卓那样的build-in的浮动按钮,所以要想在IOS里面拥有浮动按钮功能的话,得必须自己实现,很费时费力。
    我们公司里面所开发的项目,就需要在IOS里面实现一个浮动按钮。我们公司里面一个拥有多年IOS开发经验的前辈说,自己在IOS里面来实现浮动按钮功能的话,要花个好几天来实现。
    现在,利用React Native的FlexBox的特性,只需要几行代码,就能轻松实现浮动按钮。

    render() {
        return (
          <View style={{flex: 1}}>
            <ListView style={{flex: 1000}}dataSource={this.state.datasource} renderRow=   {(rowData) => <Text>{rowData}
            </Text>}/>
            <View style={{flex: 1,top: -60, alignSelf: 'flex-end', justifyContent: 'center', alignItems: 'center'}}>
              <TouchableHighlight style={{ backgroundColor: 'red', width: 50, height: 50, borderRadius: 25, justifyContent: 'center', alignItems: 'center', margin: 20}} onPress={()=>{console.log('touched');}}  >
                <Text>
                  +
                </Text>
              </TouchableHighlight>
            </View>
          </View>
    
        );
      }
    

    0_1458959925967_Screen Shot 2016-03-26 at 11.37.31 AM.png

    在上面的代码中,视图的结构如下

                             <View>
                               |
                     -----------------------
                     |                     |
                 <ListView>              <View>
                                            |                               
                                   <TouchableHighlight/>
                                            |
                                          <Text>
    

    在这个结构中,第一层的view的flex属性为1,使其充满整个屏幕,默认的flex方向为column, 在这个view里,放下了两个view,一个是ListView,一个是View, 属于linear,平行排在一个页面。他们在第一层view所占的比列为他们自己的flex属性比。例如,他们的的flex的值都为1的话,他们各自将在第一层的view内各占一半的空间,为了让Listview尽可能充满整个屏幕,在这里,我让他们的flex比为1000/1001(ListView),和1/1001(View),1001为ListView和View的flex值之和(分母越大,第二层View所占比列就越可以忽略不计),也就是我们将第一层view分了1001份,ListView占1000份,View占1份(在这里,还可以将listview的flex设置为1,而view的flex值设置为0.001)。ListView的flex值越大,第二层的view在屏幕所占的空间就可以几乎忽略不计。为什么要在第二层放上一个view呢?目的是为了有空间绘制这个浮动按钮,并且这个浮动按钮能与ListView互不影响对方接受event。

    好,通过上面的步骤,我给浮动按钮留下了绘制空间。RN有一个很有趣的绘制原理, 就是后绘制的视图组件会压在先绘制的视图组件之上。利用这个原理,我在第二层的view里面放置了一个<TouchableHighLight>组件,由于他比其他的组件都要后绘制上去,所以给它设置一个比第二层的height高的一个height值时,它就会被绘制在最上面,在上面的代码中,我给了它一个分别50的height 和width值。但若是只是这样设置的话,它只会被显示一半出来,剩下的一半被挡在了窗口外面,所以为了让他完全被显示出来,我给它的top设置了一个负值,这样它就会出现在它自己容器的外边,也就是第二层view的上面。

    在这里例子里面,我利用了flex的特性,绘制出了一个浮动的按钮,并且这个按钮并不会影响listview的任何事件,listview也不会影响这个按钮。同样,利用这个flex的特性和绘制原理,我们还可以做出安卓里面的DrawerLayout那样的从右边屏幕或者左边屏幕划出的菜单来。等以后有时间,在做出DrawerLayout的代码。

    浮动按钮的代码放在了git上:
    https://github.com/jcdby/RN_IOS_FloatingActionBar.git


  • administrators

    浮动与flexbox特性无关。关键在于你的top属性,而top属性会触发绝对定位(position: 'absolute')。
    绝对定位层不占布局空间,即所谓的悬浮层,这才是关键。



  • @sunnylqm 你好,谢谢回复。看来你没有什么耐心看懂全文呢。首先,当然,浮动是和flexbox的特性有关top是flexbox的属性,在本文里面flexbox的flex特性控制了比列,用flexbox的top属性使其浮动,但top绝对不是关键,这里,我当然知道是top的属性,使其悬浮了起来,但光是有top属性是不够的,你可以试着让listview和view换个位置,这个button一定会被listview给挡住。关键在于我使用了RN的渲染原理。因为RN的渲染原理是让后出现的component渲染在上面位置。这才是悬浮的关键。


  • administrators

    @Jcdby 因为官方没有实现绝对定位的z-index属性,而是用顺序代替了。无论是不占布局空间,还是z-index层叠顺序,都是绝对定位的范畴,与flexbox无关。


  • administrators

    很简单的一句话解释,flexbox根本没有"层叠"一说。再具体点,去掉你这个例子中所有flexbox相关的样式,只保留top(top确实不是flexbox的属性,rn的文档并不是标准的文档),仍然可以实现悬浮。



  • @sunnylqm flex和flexbox是两个概念。去掉本文中的flex属性,当然是可以实现‘悬浮’。本文想讨论的不是‘悬浮’问题,而是如何在右下角实现‘悬浮’的同时还能保证ListView能够‘充满(当然并不是完全充满,99.99+%充满)’全屏,在这一部分利用了flexbox的按照比例分块的特性,因此,也就是本文题目所说的利用flexbox的部分。
    另外,正因为官方没有实现z-index,我才这么别扭的实现了‘悬浮’。你可以把本文的flex属性都去掉试试,看看还能否保证按钮在右下角出现的同时,还能保证listview充满全屏。
    top确实是绝对定位(没有什么可继续再重复的了),但是如果你把listview和第二层的顺序交换后,就算你使用top,或者bottom定位(负值时),都会被后面的listview给覆盖。这才是我想说的,flexbox在按照顺序排列时,处于后面位置的component或者容器里面的component在绝对定位时,超出自身容器时,可以覆盖前面flexbox item的某些部分。
    还有,按照官方文档,top却是是flexbox的属性,你要说它不是,我也无话可说。不过这也不是重点。



  • @sunnylqm 管理员,我想删除此贴。经过再三测试和研究,确实是top起了关键定位作用,比起我使用flex来分块更加高效。不过这篇文章关于RN的渲染机制还是值得讨论的。同样的top值, 若是listview和Floatingbutton的顺序对调的话,就会渲染出不同的效果。所以,要想真正的的悬浮,就一定要保证所要悬浮的组件位于其他组件之后。


  • administrators

    @Jcdby 这样的帖子很好。我们非常欢迎这样的分享和交流。



  • 悬浮的组件 使用绝对布局 应该就可以实现了吧
    render() {
    return (
    <View style={{flex: 1}}>
    <ListView style={{flex: 1}}dataSource={this.state.datasource} renderRow= {(rowData) => <Text>{rowData}
    </Text>}/>
    <TouchableHighlight style={{position:'absolute',bottom:90,right:20, backgroundColor: 'red', width: 50, height: 50, borderRadius: 25, justifyContent: 'center', alignItems: 'center', margin: 20}} onPress={()=>{console.log('touched');}} >
    <Text>
    +
    </Text>
    </TouchableHighlight>
    </View>

    );
    

    }
    position:'absolute',bottom:90,right:20,-------这个 应该 就有用了



  • @毛豆学ReactNative 对的。是可以实现固定位置。需要注意的是,你想要达到悬浮效果的话,得让想悬浮的组件位于最后的位置。你可以试一下换换listview和button的顺序, 看看效果。


  • administrators

    @Jcdby 我也得纠正下我的错误,只指定top并不会触发position:'absolute',绝对定位还是需要显式声明(不过也许ios和android有差异?)



  • @sunnylqm 是的。在IOS下测试,如果不显示声明 absolute的话,会按照relative来显示。不知道你是在什么平台下测试的。


登录后回复