ANTD mobile源码分析 -- popover
最近的开发中要用到很多的各式各样的组件。但是发现ant design mobile(后面简称ANTDM)里很多的资源。于是就分析一下,学习学习。
ANTDM直接使用了typescript,没有用ES2015,不过这不会是障碍,反而是学习typescript的一个好机会。基本上可以学的开源项目里比这个好的也不多。
目录结构
Popover组件在:
|
|--components
|
|--popover
我们要分析的组件全部都在components这个目录下。
在这个目录里还包含tests, demo和style。里面分别存放测试代码、实例和样式。其他的文件包括[component name]_native.tsx和[component name].txs以及对应的index.native.tsx和index.tsx*,方便外部引入组件。
计算点击组件的位置
这个是最核心的问题了!
实现React Native的弹出菜单,需要达到在界面上的某个可点击组件上点击了之后,就可以在被点击的组件紧挨着的下方出现一个菜单(其他的计算,比如弹出菜单在左下、右下,左上,右上的位置计算暂时不提)。
用户点击了哪个组件(按钮),哪个按钮的下面就出现一个菜单(View)。这就需要确定点击组件的位置。
我们看一下index.native.tsx这个文件。文件里基本上没几行代码,直接看render方法里返回的是MenuContext等。也就是,这个文件没实现什么pop over需要的什么东西。都在import里了:
import Menu, { MenuContext, MenuOptions, MenuOption, MenuTrigger }from 'react-native-menu';
所以ANTDM的源码分析到此为止。
我们要跳到react-native-menu。我们分析代码的方式就是无限递归,一直找到实现功能的代码为止。那么我们就可以分析react-native-menu了。
react-native-menu
这个项目的写法也是很不同。用的是比较老的ES5的React版本。github地址在这里。
这个项目里很多的文件,各位可以后面慢慢看。我们来看makeMenuContext.js。
在这个项目里,除了index.js之外都是叫做makeXXX.js。里面都是HOC的实现方式。而且更加Trick的是HOC的前两个参数是React和ReactNative。
回到makeMenuContext.js,在openMenu()这个方法里就有实现的方式。这就是我们寻找代码递归跳出的地方。我们来看一下实现方式:
openMenu(name) {
const handle = ReactNative.findNodeHandle(this._menus[name].ref);
UIManager.measure(handle, (x, y, w, h, px, py) => {
this._menus[name].measurements = { x, y, w, h, px, py };
this.setState({
openedMenu: name,
menuOptions: this._makeAndPositionOptions(name, this._menus[name].measurements),
backdropWidth: this._ownMeasurements.w
});
this._activeMenuHooks = this._menus[name];
this._activeMenuHooks && this._activeMenuHooks.didOpen();
});
},
这里使用了UIManager,来自:
const {
UIManager,
TouchableWithoutFeedback,
ScrollView,
View,
BackHandler
} = ReactNative
用现代一点的写法的话就是:import { UIManager } from 'react-native';。
使用的时候是这么用的:
const handle = ReactNative.findNodeHandle(this._menus[name].ref);
UIManager.measure(handle, (x, y, w, h, px, py) => {
// x, y, width, height, pageX, pageY
});
measure()方法的回调里得到的就是该组件对于Screen的位置。还有其他的measureXXX()方法在这里可以看到。
measure得到的x,y,w,h,px,py是这个组件的左上角坐标(x,y)和宽、高。在这个measure方法里得到的px和py与这个组件的左上角坐标值一样。
注意:measure的时候,只有在原生视图完成绘制之后才会返回值。
所以,如果要快点得到一个组件在screen上的坐标值的话,那么可以这样:
<View onLayout={this.onLayout}>
</View>
// onLayout
onLayout() {
const handle = ReactNative.findNodeHandle(this.refs.Container);
UIManager.measure(handle, (x, y, w, h, px, py) => {
this._ownMeasurements = {x, y, w, h, px, py};
});
}
所以,在弹出菜单的组件上使用onLayoutprops得到它的位置。
注意:
they(measureXXX方法) are not available on composite components that aren't directly backed by a native view.
大意是,如果组合组件的最外层不是一个原生view的话,measureXXX()方法是没法用的!!
那么measure方法的第一个参数,也就是measure的目标组件如何获得呢?代码在这里:const handle = ReactNative.findNodeHandle(this._menus[name].ref);。在findNodeHandle()方法的参数是组件的ref。那么,通过组件的ref可以得到组件的handle。在通过这个handle就可以来measure组件,得到这个组件的位置、宽高等数据。
到这里我们就知道如何来算出触发组件的位置了。但是,这个直接使用UIManager的方法太复杂了。
基本上,组件可以直接调用measure方法。我们来简单的实现一下这个弹出菜单的功能。
Reimplement
不管单词对错了。总之是重写一次。简化版的!为了篇幅足够长,我就把代码都贴出来了。哈哈~
/**
* Created by Uncle Charlie, 2018/03/01
* @flow
*/
import React from 'react';
import { TouchableOpacity, Text, View, StyleSheet } from 'react-native';
type Prop = {
text: ?string,
onPress: (e?: any) => void,
styles?: { button: any, text: any },
};
export default class Button extends React.Component<Prop, {}> {
static defaultProps = {
text: 'Show Menu',
};
handlePress = () => {
const { onPress } = this.props;
if (!this.container) {
console.error('container view is empty');
return;
}
this.container.measure((x, y, w, h, px, py) => {
console.log('===>measure', { x, y, w, h, px, py });
onPress && onPress({ left: x, top: y + h });
});
};
onLayout = () => {};
render() {
const { text, styles } = this.props;
const wrapper =
styles && styles.wrapper ? styles.wrapper : innerStyles.wrapper;
return (
<View
style={wrapper}
onLayout={this.onLayout}
ref={container => (this.container = container)}
>
<TouchableOpacity onPress={this.handlePress}>
<View>
<Text>{text}</Text>
</View>
</TouchableOpacity>
</View>
);
}
}
const innerStyles = StyleSheet.create({
wrapper: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'green',
},
});
这个简化版的实现思路就是:
- 点击按钮(
TouchableOpacity)的时候measure按钮组件 - 把measure出来的按钮组件的位置作为参数发送给父组件
- 父组件在计算后的位置显示menu
measure
在measure组件之前,首先要获得这个组件的ref。
render() {
// ...
return (
<View ref={container => (this.container = container)}
>
// ...
</View>
);
}
得到的ref就是this.container。
handlePress = () => {
const { onPress } = this.props;
if (!this.container) {
console.error('container view is empty');
return;
}
this.container.measure((x, y, w, h, px, py) => {
console.log('===>measure', { x, y, w, h, px, py });
onPress && onPress({ left: x, top: y + h });
});
};
在点击按钮之后开始measure。直接在获得的ref上调用measure方法就可以:this.container.measure。获得measure的结果之后,调用props传过来的方法onPress把需要用到的数据传过去。
绘制Menu
renderMenu = () => {
const { top, left, open } = this.state;
if (!open) {
return null;
}
return (
<View
style={{
position: 'absolute',
left,
top,
width: 100,
height: 200,
backgroundColor: 'rgba(52, 52, 52, 0.8)',
}}
>
<Text>Menu</Text>
</View>
);
};
我们要View显示在一个特定的位置的时候,需要在style里设置位置模式为position: 'absolute',也就是启用绝对定位。
上面的left、和top就是菜单的具体位置。宽、高暂时hard code了(简化版。。。)。
这样就一个popover,超级简化版的,就完成了。全部的代码在这里。
最后
我们在前文中说道过一个更好的获得触发组件的位置的方式,onLayout。这个方法是空的。各位可以试着完成这个方法,或者全部完成这个popover组件作为练习。
ANTD mobile源码分析 -- popover的更多相关文章
- BOOtstrap源码分析之 tooltip、popover
一.tooltip(提示框) 源码文件: Tooltip.jsTooltip.scss 实现原理: 1.获取当前要显示tooltip的元素的定位信息(top.left.bottom.right.wid ...
- Go Mobile 例子 audio 源码分析
看这个源码分析前,建议先看更简单地例子 basic 的源码分析(http://www.cnblogs.com/ghj1976/p/5183199.html), 一些基础知识本篇将不再提及. audio ...
- golang.org/x/mobile/exp/gl/glutil/glimage.go 源码分析
看这篇之前,建议先看之前几篇,这几篇是基础. Go Mobile 例子 basic 源码分析 http://www.cnblogs.com/ghj1976/p/5183199.html OpenGL ...
- Go Mobile 例子 basic 源码分析
OpenGL ES(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集,针对手机.PDA和游戏主机等嵌入式设备而设计.该API由Khronos集团定义推广, ...
- gRPC源码分析0-导读
gRPC是Google开源的新一代RPC框架,官网是http://www.grpc.io.正式发布于2016年8月,技术栈非常的新,基于HTTP/2,netty4.1,proto3.虽然目前在工程化方 ...
- gomoblie flappy 源码分析:游戏逻辑
本文主要讨论游戏规则逻辑,具体绘制技术请参看相关文章: gomoblie flappy 源码分析:图片素材和大小的处理 http://www.cnblogs.com/ghj1976/p/5222289 ...
- ZRender源码分析3:Painter(View层)-上
回顾 上一篇说到:ZRender源码分析2:Storage(Model层),这次咱看来看看Painter-View层 总体理解 Painter这个类主要负责MVC中的V(View)层,负责将Stora ...
- Zepto源码分析(二)奇淫技巧总结
Zepto源码分析(一)核心代码分析 Zepto源码分析(二)奇淫技巧总结 目录 * 前言 * 短路操作符 * 参数重载(参数个数重载) * 参数重载(参数类型重载) * CSS操作 * 获取属性值的 ...
- 一步步实现windows版ijkplayer系列文章之三——Ijkplayer播放器源码分析之音视频输出——音频篇
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
随机推荐
- 已知有两个水杯,一个11L一个7L,水可以任意使用,求怎么得到2L 的详细解法
问题:有两个水杯,一个是11L一个是7L,水可以随便用,怎么得到2L 1.了解问题的本质 问题中给出了两个杯子,只有这两个杯子有量度,所以只能让杯中的水满进满出才能确定杯子中最后有多少水. 现在问题要 ...
- 使用redis所维护的代理池抓取微信文章
搜狗搜索可以直接搜索微信文章,本次就是利用搜狗搜搜出微信文章,获得详细的文章url来得到文章的信息.并把我们感兴趣的内容存入到mongodb中. 因为搜狗搜索微信文章的反爬虫比较强,经常封IP,所以要 ...
- phpstorm中使用xdebug配置cli模式的调试
这里略去xdebug的安装,安装很简单可以下载源码包,动态编译进去! 环境: Dev 服务器(IP:192.168.2.100),安装phpstorm,用来做开发任务! Server服务器(IP:19 ...
- Spring基础篇——bean的自动化装配
上篇博文讲Spring的IOC容器时说道,虽然容器功能强大,但容器本身只是个空壳,需要我们主动放入装配对象,并告诉它对象之间的协作关系,然后容器才能按照我们的指示发挥它的魔力,完成装配bean的使命. ...
- Spring MVC Mock demo
package com.niwodai.mem.web.controller; import com.alibaba.fastjson.JSON; import org.junit.Before; i ...
- 分布式唯一id:snowflake算法思考
匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续R ...
- Linux CentOS 7 下 JDK 1.7 安装与配置
前言 简单记录一下在CentOS 7中安装配置JDK 1.7的全过程~ 下载 首先是jdk 1.7 64bit & 32bit的下载地址: jdk-7u79-linux-x64.tar.gz ...
- 制作 alipay-sdk-java包到本地仓库
一.首先 搭建好maven 基础环境,本地可以运行maven 命令 从支付宝官网上下载sdk https://doc.open.alipay.com/doc2/detail?treeId=54& ...
- BZOJ 1022: [SHOI2008]小约翰的游戏John [SJ定理]
传送门 $anti-nim$游戏,$SJ$定理裸题 规定所有单一游戏$sg=0$结束 先手必胜: $1.\ sg \neq 0,\ 某个单一游戏sg >1$ $2.\ sg = 0,\ 没有单一 ...
- Kafka基本知识回顾及复制
Producers发布记录到集群,集群维护这些记录并且将记录分发给Consumers. 在Kafka中,最关键的抽象是topic.Producers发布记录到一个topic,Consumers订阅一个 ...