前言
在RN开发中仅仅使用flex布局,也满足不了我们日常的需求开发;RN官方也提供了定位布局,flexbox定位和position定位可以同时使用,同时生效;
position
RN提供了两种布局方式:absolute和relative
- relative
- 相对于 上一个兄弟节点
- 不可以浮动(尽管偏移了,还是占了一个位置)
- absolute
- 相对于 父视图
- 浮动的
绝对布局是脱离文档流的,不过RN依旧在文档层次结构里面,这个和html的position也很大不一样。另外还有一个和html不一样的是,html中position:absolute要求父容器的position必须是absolute或者relative,如果第一层父容器position不是absolute或者relative,在html会依次向上递归查询直到找到为止,然后居于找到的父容器绝对定位。
如果你之前是搞安卓开发的会觉得RN设计非常怪异,在我们安卓原生开发中,决定用什么布局的是由parent决定的,如:AbsoluteLayout和RelativeLayout,而在RN开发中,决定用什么布局的是有child来决定的。
比如:<Text style={{position:‘absolute’}}> 这样Text组件相对于外层parent组件是相对布局,同理我们可以修改成relative。
可以使用left、top、right、bottom来改变偏移量。如下图:
absolute:
view1的样式: { position:'absolute',left:5,top:5 }.
view2的样式: { position:'absolute',right:10,bottom:10 }.
relative:
view1的样式: { position:'relative',left:5,top:5 }.
view2的样式: { position:'relative',left:50,top:30 }.
zIndex层级
zIndex是rn在0.30开始支持的属性,是可以生效的;
一般是zIndex层级大的在上面
对于Android,两个同一层级的定位组件(position:“absolute”)
情况 | 在z轴的层叠关系 |
---|---|
既没有ZIndex属性,又没有elevation 属性 | 由其摆放位置决定的,放在下面的组件会在上层 |
两个组件只有zIndex没有elevation属性时 | zIndex大的在上层 |
两个组件有elevation属性 | elevation大的在上层 |
两个组件既有zIndex属性elevation属性 | 以elevation为准 |
注:对于IOS,同层级的组件,z轴的层叠关系只与摆放顺序与zIndex有关,与elevation无关
Android和iOS position差异
如果使用position:"absolute"想要把子元素浮出到父元素外面一部分,在html中的常规处理可以把偏移量top、left、right、bottom设置成负值即可;此种方案在RN的iOS端可是可行的,但是Android端浮出父元素外面的部分是不显示的;
RN对absolute的官方解释就是:以父元素的边框为基准进行偏移,也就是说只能在父元素包裹的范围内偏移;
一下是iOS和Android上的差异:
iOS
Android
如上图想要给其中的一个模块打上一个推荐位的标,实现代码如下:
priceGroup: {
paddingHorizontal: 15,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginTop: 25,
position: "relative",
zIndex: 99998,
elevation: 99998
},
//父级容器
commotView: {
width: 109,
height: 100,
justifyContent: "center",
alignItems: "center",
borderWidth: 1
},
//推荐logo
recIcon: {
width: 38,
height: 24,
position: "absolute",
top: -18,
right: -5,
justifyContent: "center",
alignItems: "center",
zIndex: 99999,
elevation: 99999
}
let priceDom = list.map((item, index) => {
//价格列表数据对象 dealPrice折扣价 originPrice原价 desc标题
let { id, dealPrice, originPrice, desc, recommand } = item;
//是否选中
let isChecked = id == sel.id ? true : false;
//是否使能 免费使用不选点击选择
let dis = discountStatus == "0" ? true : false;
return (
<TouchableOpacity
activeOpacity={1}
key={index}
onPress={() => {
if (discountStatus) {
this.setState((pre) => {
return {
selected: item
};
});
}
}}
style={[
styles.commotView,
//选中状态和非选中状态以及免费不能点击状态字体颜色区分
isChecked
? styles.activePriceView
: dis
? styles.disableView
: styles.priceView
]}>
{/* 折扣状态和全价状态下会显示推荐tag */}
{discountStatus && recommand ? (
<Image
source={require("./../img/recommand.png")}
style={styles.recIcon}
/>
) : null}
{/* 订阅列表标题栏 */}
<Text
style={{
color: isChecked
? COLOR.ActivetilColor
: dis
? COLOR.disColor
: COLOR.priceColor,
fontSize: 15
}}>
{desc}
</Text>
{/* 订阅列表当前价格 折扣状态显示折扣之后的价格
免费试用显示全价中间价贯穿线 全价直接展示原始价格 */}
<Text
style={{
color: isChecked
? COLOR.ActivetilColor
: dis
? COLOR.disColor
: COLOR.priceColor,
fontSize: 18
}}>
¥
<Text
style={[
{ fontSize: 36, fontWeight: "700" },
discountStatus ? null : styles.lineThrough
]}>
{discountStatus == 1 ? parseInt(dealPrice) : parseInt(originPrice)}
</Text>
</Text>
{/* 折扣状态下的原始价格展示 */}
{discountStatus == 1 ? (
<Text
style={[
styles.lineThrough,
isChecked ? styles.activeDiscountText : styles.discountText
]}>
{parseInt(originPrice)}元
</Text>
) : null}
</TouchableOpacity>
);
});
return <View style={styles.priceGroup}>{priceDom}</View>;
整体思路如下:
此种布局就是常规思路,把要logo作为一个子元素放在要打标的模块里面,作为一个子元素,然后使用定位达到UI设计效果;二父元素的尺寸又是严格按照UI要求的尺寸来固定大小了;此时超出父元素的部分在Android上就不会显示了;
解决方案:
//原布局方式
父级标签里面直接渲染当前全部内容,把推荐标签页包裹在内
<View parent>
<Image tag/>
//渲染内容content
</View>
//修改后
<View parent>
<Image child1/>
<View child2>
//渲染内容content
</View>
</View>
修改方式:
- 把原父级区块放在一个视图里面 复用原父级样式 成一个新模块 child2
- 把tag推荐图片作为一个兄弟节点 child1
- 把child1和child2放到一个视图里面 parent
- parent布局:
- 如果是如上图所示的需要上浮和右浮出
- parent大小设置 width = child2.width + child1右浮宽度
height = child2.widt + child1上浮高度 - 设置完parent容器大小之后,容器内布局
水平方向:从左往右布局
垂直方向:从下往上布局 - 其余方向同上 自行使用
以上是本人在开发中的一些拙见,有不足支持欢迎指正。