React Native仿美团下拉菜单的实例代码

本文介绍了React Native仿美团下拉菜单的实例代码,最近也在学习React Native,顺便分享给大家

在很多产品中都会涉及到下拉菜单选择功能,用的最好的当属美团了,其效果如下:

要实现上面的效果,在原生中比较好做,直接使用PopWindow组件即可。如果使用React Native开发上面的效果,需要注意几个问题:

1、 在下拉的时候有动画过度效果;

2、下拉菜单出现后点击菜单项,菜单项可选择,并触发对应的事件;

3、下拉菜单中的项目可以配置;

要实现弹框效果,我们马上回想到使用,而要绘制打钩图标和下拉三角,我们首先想到使用ART实现,当然选择使用图标也是可以的。例如使用ART绘制对勾的代码如下:

{ return ( ); }

下拉动画的实现上,需要使用Animated。例如,背景颜色变化需要使用Animated.timing。

运行效果:

本示例设计三个文件:导航栏FoodActionBar.js,下拉弹框TopMenu.js和文件主类FoodView.js。

FoodActionBar.js

import React,{Component} from 'react';
import {Platform,View,Dimensions,Text,StyleSheet,TouchableOpacity,Image} from 'react-native';
import px2dp from '../util/Utils'

const isIOS = Platform.OS == "ios"
const {width,height} = Dimensions.get('window')
const headH = px2dp(isIOS ? 64 : 44)

export default class FoodActionBar extends Component {

constructor(props) {
super(props);
this.state = {
showPop: false,}
}

renderHeader() {
return (

输入商家名、品类和商圈 { this.setState({ showPop: !this.state.showPop }) }}> ) }

render() {
return (

{this.renderHeader()} ); } }

const styles = StyleSheet.create({
headerStyle: {
backgroundColor: "#ffffff",height: headH,paddingTop: px2dp(isIOS ? 20 : 0),flexDirection: 'row',alignItems: 'center',},searchBar: {
flex:1,height: 30,borderRadius: 19,backgroundColor:'#e9e9e9',marginLeft: 10,justifyContent: 'flex-start',alignSelf: 'center',paddingLeft: 10,text: {
fontSize: 16,color: '#ffffff',justifyContent: 'center',iconStyle: {
width: 22,height: 22,action: {
flexDirection: 'row',marginLeft:10,marginRight:10
},scanIcon: {
width: 28,height: 28,scanText: {
fontSize: 14,});

TopMenu.js

import React,{Component} from 'react';
import {
AppRegistry,Animated,ScrollView,PixelRatio,TouchableWithoutFeedback,TouchableHighlight,ART,View
} from 'react-native';

const {Surface,Shape,Path,Group} = ART;

const {width,height} = Dimensions.get('window');

const T_WIDTH = 7;
const T_HEIGHT = 4;

const COLOR_HIGH = '#00bea9';
const COLOR_NORMAL = '#6c6c6c';

const LINE = 1 / PixelRatio.get();

class Triangle extends React.Component {

render() {

var path;
var fill;
if (this.props.selected) {
  fill = COLOR_HIGH;
  path = new Path()
    .moveTo(T_WIDTH / 2,0)
    .lineTo(0,T_HEIGHT)
    .lineTo(T_WIDTH,T_HEIGHT)
    .close();
} else {
  fill = COLOR_NORMAL;
  path = new Path()
    .moveTo(0,0)
    .lineTo(T_WIDTH,0)
    .lineTo(T_WIDTH / 2,T_HEIGHT)
    .close();
}

return (
  <Surface width={T_WIDTH} height={T_HEIGHT}>
    <Shape d={path} stroke="#00000000" fill={fill} strokeWidth={0}/>
  </Surface>
)

}
}

const TopMenuItem = (props) => {
const onPress = () => {
props.onSelect(props.index);
}
return (

{props.label} ); };

const Subtitle = (props) => {
let textStyle = props.selected ?
[styles.tableItemText,styles.highlight,styles.marginHigh] :
[styles.tableItemText,styles.margin];

let rightTextStyle = props.selected ? [styles.tableItemText,styles.highlight] : styles.tableItemText;

let onPress = () => {
props.onSelectMenu(props.index,props.subindex,props.data);
}

return (
<TouchableHighlight onPress={onPress} underlayColor="#f5f5f5">

{props.selected && } {props.data.title} {props.data.subtitle} ); };

const Title = (props) => {
let textStyle = props.selected ?
[styles.tableItemText,styles.highlight] : styles.tableItemText;

let onPress = () => {
props.onSelectMenu(props.index,props.data);
}

return (
<TouchableHighlight onPress={onPress} underlayColor="#f5f5f5">

{props.selected && } {props.data.title} ); };

const Check = () => {
return (
<Surface
width={18}
height={12}

); }

export default class TopMenu extends Component {

constructor(props) {
super(props);
let array = props.config;
let top = [];
let maxHeight = [];
let subselected = [];
let height = [];
//最大高度
var max = parseInt((height - 80) * 0.8 / 43);

for (let i = 0,c = array.length; i < c; ++i) {
  let item = array[i];
  top[i] = item.data[item.selectedIndex].title;
  maxHeight[i] = Math.min(item.data.length,max) * 43;
  subselected[i] = item.selectedIndex;
  height[i] = new Animated.Value(0);
}


//分析数据
this.state = {
  top: top,maxHeight: maxHeight,subselected: subselected,height: height,fadeInOpacity: new Animated.Value(0),selectedIndex: null
};

}

componentDidMount() {

}

createAnimation = (index,height) => {
return Animated.timing(
this.state.height[index],{
toValue: height,duration: 250
}
);
}

createFade = (value) => {
return Animated.timing(
this.state.fadeInOpacity,{
toValue: value,duration: 250,}
);
}

onSelect = (index) => {
if (index === this.state.selectedIndex) {
//消失
this.hide(index);
} else {
this.setState({selectedIndex: index,current: index});
this.onShow(index);
}
}

hide = (index,subselected) => {
let opts = {selectedIndex: null,current: index};
if (subselected !== undefined) {
this.state.subselected[index] = subselected;
this.state.top[index] = this.props.config[index].data[subselected].title;
opts = {selectedIndex: null,current: index,subselected: this.state.subselected.concat()};
}
this.setState(opts);
this.onHide(index);
}

onShow = (index) => {

Animated.parallel([this.createAnimation(index,this.state.maxHeight[index]),this.createFade(1)]).start();

}

onHide = (index) => {
//其他的设置为0
for (let i = 0,c = this.state.height.length; i < c; ++i) {
if (index != i) {
this.state.height[i].setValue(0);
}
}
Animated.parallel([this.createAnimation(index,0),this.createFade(0)]).start();

}

onSelectMenu = (index,subindex,data) => {
this.hide(index,subindex);
this.props.onSelectMenu && this.props.onSelectMenu(index,data);
}

renderList = (d,index) => {
let subselected = this.state.subselected[index];
let Comp = null;
if (d.type == 'title') {
Comp = Title;
} else {
Comp = Subtitle;
}

let enabled = this.state.selectedIndex == index || this.state.current == index;

return (
  <Animated.View key={index} pointerEvents={enabled ? 'auto' : 'none'}
          style={[styles.content,{opacity: enabled ? 1 : 0,height: this.state.height[index]}]}>
    <ScrollView style={styles.scroll}>
      {d.data.map((data,subindex) => {
        return <Comp
          onSelectMenu={this.onSelectMenu}
          index={index}
          subindex={subindex}
          data={data}
          selected={subselected == subindex}
          key={subindex}/>
      })}
    </ScrollView>
  </Animated.View>
);

}

render() {
let list = null;
if (this.state.selectedIndex !== null) {
list = this.props.config[this.state.selectedIndex].data;
}
console.log(list);
return (
<View style={{flex: 1}}>

{this.state.top.map((t,index) => { return })} {this.props.renderContent()} {this.props.config.map((d,index) => { return this.renderList(d,index); })} ); } }

const styles = StyleSheet.create({

scroll: {flex: 1,backgroundColor: '#fff'},bgContainer: {position: 'absolute',top: 40,width: width,height: height},bg: {flex: 1,backgroundColor: 'rgba(50,50,0.2)'},content: {
position: 'absolute',width: width
},highlight: {
color: COLOR_HIGH
},marginHigh: {marginLeft: 10},margin: {marginLeft: 28},titleItem: {
height: 43,paddingRight: 10,borderBottomWidth: LINE,borderBottomColor: '#eee',tableItem: {
height: 43,justifyContent: 'space-between'
},tableItemText: {fontWeight: '300',fontSize: 14},row: {
flexDirection: 'row'
},item: {
flex: 1,menuTextHigh: {
marginRight: 3,fontSize: 13,color: COLOR_HIGH
},menuText: {
marginRight: 3,color: COLOR_NORMAL
},topMenu: {
flexDirection: 'row',height: 40,borderTopWidth: LINE,borderTopColor: '#bdbdbd',borderBottomWidth: 1,borderBottomColor: '#f2f2f2'
},});

主类FoodView.js:

import React,View
} from 'react-native';
const {width,height} = Dimensions.get('window');

import FoodActionBar from "./pop/FoodActionBar";
import Separator from "./util/Separator";
import TopMenu from "./pop/TopMenu";

const CONFIG = [
{
type:'subtitle',selectedIndex:1,data:[
{title:'全部',subtitle:'1200m'},{title:'自助餐',subtitle:'300m'},subtitle:'200m'},subtitle:'500m'},subtitle:'800m'},subtitle:'700m'},subtitle:'900m'},]
},{
type:'title',selectedIndex:0,data:[{
title:'智能排序'
},{
title:'离我最近'
},{
title:'好评优先'
},{
title:'人气最高'
}]
}
];

export default class FoodView extends Component {

constructor(props){
super(props);
this.state = {
data:{}
};
}

renderContent=()=>{
return (

index:{this.state.index} subindex:{this.state.subindex} title:{this.state.data.title} ); // alert(this.state.data.title) };

onSelectMenu=(index,data)=>{
this.setState({index,data});
};

render() {
return (

); } }

const styles = StyleSheet.create({
container: {
flex: 1,width:width,backgroundColor: '#F5FCFF',text: {
fontSize:20,marginTop:100,});

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


kindeditor4.x代码高亮功能默认使用的是prettify插件,prettify是Google提供的一款源代码语法高亮着色器,它提供一种简单的形式来着色HTML页面上的程序代码,实现方式如下: 首先在编辑器里面插入javascript代码: 确定后会在编辑器插入这样的代码: <pre
这一篇我将介绍如何让kindeditor4.x整合SyntaxHighlighter代码高亮,因为SyntaxHighlighter的应用非常广泛,所以将kindeditor默认的prettify替换为SyntaxHighlighter代码高亮插件 上一篇“让kindeditor显示高亮代码”中已经
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面
如何用js控制图片放大缩小
JS怎么获取当前时间戳
JS如何判断对象是否为数组
JS怎么获取图片当前宽高
JS对象如何转为json格式字符串
JS怎么获取图片原始宽高
怎么在click事件中调用多个js函数
js如何往数组中添加新元素
js如何拆分字符串
JS怎么对数组内元素进行求和
JS如何判断屏幕大小
js怎么解析json数据
js如何实时获取浏览器窗口大小
原生JS实现别踩白块小游戏(五)
原生JS实现别踩白块小游戏(一)