场景
当出现下面这种布局:绿色元素的高度是由红色元素高度撑开,红色元素又需要超出绿色元素一部分(这里是以超出100像素为例子),并且给红色元素添加了点击事件。
我们一般会把红色元素放到绿色元素里面,形成父子关系,然后对红色元素设置偏移量 top:-100(或者marginTop:-100)。
less
<View style={styles.greenView}>
<TouchableWithoutFeedback onPress={onPress}>
<View style={styles.redView}></View>
</TouchableWithoutFeedback>
</View>
greenView: {
width: '100%',
alignItems:'center',
backgroundColor: 'green',
},
redView: {
width: 180,
height:300,
backgroundColor: 'red',
top:-100,
// marginTop:-100,
},
问题
上面这种父子关系在 安卓 中会出现红色元素超出绿色元素的部分(那100像素)的点击事件是无法生效的,ios是正常的。
原因就是: 安卓对于超出父容器的子容器的view不给予事件分发
所以我们就要避免将红色元素包裹在绿色元素里面从而出现父子关系这种写法。
解决方案
下面给出2中解决方案
方案1(最佳方案)
-
- 步骤1:让红色容器和绿色容器并列,避免出现子父关系。
- 步骤2: 然后给绿色容器设置paddingTop:300(这里300是红色元素的高度),或者在绿色容器里面放置一个高度等于红色元素高度的view元素,目的就是给红色元素占位,撑开绿色元素的高度。
【注意:一定要让红色元素的层级比绿色元素高,不然红色元素显示不出来】
-
- 步骤3: 然后给红色元素设置marginBottom【不建议使用bottom】等于负的自身的高度(这里是-300像素)。这样红色容器在视觉上就显示在绿色容器里面,在此基础上再给红色元素的marginBottom加上红色元素需要超出绿色元素的高度就行(这里是加100) 。
步骤1图 步骤2图 步骤3图
less
<TouchableWithoutFeedback onPress={onPress}>
<View style={styles.redView}></View>
</TouchableWithoutFeedback>
<View style={styles.greenView}></View>
greenView: {
width: '100%',
height: 300,
alignItems:'center',
backgroundColor: 'green',
paddingTop:300, // 值为红色元素的高度300
},
redView: {
width: 180,
height:300,
backgroundColor: 'red',
marginBottom:-300 + 100, // 值为负的自身的高度再加上需要超出的高度
zIndex:1, // 注意红色元素的层级要比绿色元素高,不然显示不出来
},
为什么说不建议用bottom来设置红色元素的偏移位置,它和marginBottom都可以达到这种果。因为如果用bottom的话,会影响其它元素的布局。
我们在绿色元素的同级增加一个蓝色元素,就可以看到明显区别。设置MarginBottom的样式 往往是我们所期待的,蓝色元素紧挨着红色元素。而设置bottom会使的红色元素的起始位置的地方无法被其它元素占用。蓝色元素与红色元素之间间隔了200像素
less
<View style={styles.othersView}></View>
<TouchableWithoutFeedback onPress={onPress}>
<View style={styles.redView}></View>
</TouchableWithoutFeedback>
<View style={styles.greenView}></View>
othersView:{
flex:1,
width:'100%',
backgroundColor:'blue'
},
greenView: {
width: '100%',
height: 300,
alignItems:'center',
backgroundColor: 'green',
paddingTop:300,
},
redView: {
width: 180,
height:300,
backgroundColor: 'red',
zIndex:1,
marginBottom: -300 + 100, // 左图样式
bottom:-300 + 100, // 右图样式
},
方案2: (局限性)
如果你就是想设计成父子关系的话,也可以实现这种效果。但是只适合安卓端。
-
- 可以给父容器设置boderTopWidth,为红色容器需要超出的部分(这里是100像素),然后把再把绿色元素的border颜色设置为透明色就行。只要子容器没有超出父容器,点击事件就是正常的。【但是这里必须要设置一下borderRadius不为0即可】
左图安卓,右图iios,都给绿色元素设置了borderTopWidth: 100,borderColor: 'transparent',
borderRadius:1。
安卓端的border默认为黑色,设置borderColor: 'transparent' 后再变成透明色。
ios端的border默认为背景颜色(绿色),设置borderColor: 'transparent'还是为背景色(绿色)
【注意:如果borderRadius没值的话,双端border的颜色都是默认为黑色,再把border设置为透明色就都是显示出背景的颜色】
安卓端图 ios端图
less
<View style={styles.othersView}></View>
<View style={styles.greenView}>
<TouchableWithoutFeedback onPress={onPress}>
<View style={styles.redView} ></View>
</TouchableWithoutFeedback>
</View>
othersView:{
flex:1,
width:'100%',
backgroundColor:'blue'
},
greenView: {
width: '100%',
alignItems:'center',
backgroundColor: 'green',
borderTopWidth: 100,
borderRadius: 1, // 不为0即可
borderColor:'transparent',
},
redView: {
width: 180,
height:300,
backgroundColor: 'red',
top: -100,
},
总结
安卓端会出现子容器超出父容器部分的点击事件不生效,是因为安卓对于超出父容器的子容器的view不给予事件分发,因此在开发中,如果有类似场景需求,要避免这种父子关系的出现。上面我是以超出部分在绿色元素的上方为例子,如果红色元素要超出的部分在绿色元素的左边、右边或者下边,方法是类似的。整体思路就是让所谓的子元素(红色元素)在父元素(绿色元素)外面,变成兄弟关系,然后再想办法让子元素(红色元素)再偏移回父元素(绿色元素)里面。最后在视觉上就形成了父子关系的效果。