当前位置: 移动技术网 > IT编程>开发语言>JavaScript > react native带索引的城市列表组件的实例代码

react native带索引的城市列表组件的实例代码

2017年12月12日  | 移动技术网IT编程  | 我要评论

城市列表选择是很多app共有的功能,比如典型的美图app。那么对于react native怎么实现呢?

这里写图片描述

要实现上面的效果,首先需要对界面的组成简单分析,界面的数据主要由当前城市,历史访问城市和热门城市组成,所以我们在提供json数据的时候就需要将数据分为至少3部分。

const all_city_list = data_json.allcitylist;
const hot_city_list = data_json.hotcitylist;
const last_visit_city_list = data_json.lastvisitcitylist;

而要实现字母索引功能,我们需要自定义一个控件,实现和数据的绑定关系,自定义组件代码如下:

cityindexlistview.js

'use strict';
import react, {component} from 'react';
import {
  stylesheet,
  view,
  text,
  touchableopacity,
  listview,
  dimensions,
} from 'react-native';

import toast, {duration} from './toastutil'

const sectionheight = 30;
const rowheight = 40;
const rowheight_box = 40;
var totalheight = []; //每个字母对应的城市和字母的总高度

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

var that;

const key_now = '当前';
const key_last_visit = '最近';
const key_hot = '热门';

export default class cityindexlistview extends component {

  constructor(props) {
    super(props);

    var getsectiondata = (datablob, sectionid) => {
      return sectionid;
    };
    var getrowdata = (datablob, sectionid, rowid) => {
      return datablob[sectionid][rowid];
    };

    let all_city_list = this.props.allcitylist;
    let current_city_list = this.props.nowcitylist;
    let last_visit_city_list = this.props.lastvisitcitylist;
    let hot_city_list = this.props.hotcitylist;

    let letterlist = this._getsortletters(all_city_list);

    let datablob = {};
    datablob[key_now] = current_city_list;
    datablob[key_last_visit] = last_visit_city_list;
    datablob[key_hot] = hot_city_list;

    all_city_list.map(cityjson => {
      let key = cityjson.sortletters.touppercase();

      if (datablob[key]) {
        let sublist = datablob[key];
        sublist.push(cityjson);
      } else {
        let sublist = [];
        sublist.push(cityjson);
        datablob[key] = sublist;
      }
    });

    let sectionids = object.keys(datablob);
    let rowids = sectionids.map(sectionid => {
      let thisrow = [];
      let count = datablob[sectionid].length;
      for (let ii = 0; ii < count; ii++) {
        thisrow.push(ii);
      }

      let eachheight = sectionheight + rowheight * thisrow.length;
      if (sectionid === key_hot || sectionid === key_now || sectionid === key_last_visit) {
        let rownum = (thisrow.length % 3 === 0)
          ? (thisrow.length / 3)
          : parseint(thisrow.length / 3) + 1;

        console.log('sectionids===>' + sectionids + ", rownum=====>" + rownum);

        eachheight = sectionheight + rowheight_box * rownum;
      }

      totalheight.push(eachheight);

      return thisrow;
    });


    let ds = new listview.datasource({
      getrowdata: getrowdata,
      getsectionheaderdata: getsectiondata,
      rowhaschanged: (row1, row2) => row1 !== row2,
      sectionheaderhaschanged: (s1, s2) => s1 !== s2
    });

    this.state = {
      datasource: ds.clonewithrowsandsections(datablob, sectionids, rowids),
      letters: sectionids
    };

    that = this;
  }

  _getsortletters(datalist) {
    let list = [];

    for (let j = 0; j < datalist.length; j++) {
      let sortletters = datalist[j].sortletters.touppercase();

      let exist = false;
      for (let xx = 0; xx < list.length; xx++) {
        if (list[xx] === sortletters) {
          exist = true;
        }
        if (exist) {
          break;
        }
      }
      if (!exist) {
        list.push(sortletters);
      }
    }

    return list;
  }

  _citynameclick(cityjson) {
    // alert('选择了城市====》' + cityjson.id + '#####' + cityjson.name);
    this.props.onselectcity(cityjson);
  }

  _scrollto(index, letter) {
    this.refs.toast.close();
    let position = 0;
    for (let i = 0; i < index; i++) {
      position += totalheight[i]
    }
    this._listview.scrollto({y: position});
    this.refs.toast.show(letter, duration.length_short);
  }

  _renderrightletters(letter, index) {
    return (
      <touchableopacity key={'letter_idx_' + index} activeopacity={0.6} onpress={() => {
        this._scrollto(index, letter)
      }}>
        <view style={styles.letter}>
          <text style={styles.lettertext}>{letter}</text>
        </view>
      </touchableopacity>
    );
  }

  _renderlistbox(cityjson, rowid) {
    return (
      <touchableopacity key={'list_item_' + cityjson.id} style={styles.rowviewbox} onpress={() => {
        that._citynameclick(cityjson)
      }}>
        <view style={styles.rowdatabox}>
          <text style={styles.rowdatatextbox}>{cityjson.name}</text>
        </view>
      </touchableopacity>
    );
  }

  _renderlistrow(cityjson, rowid) {
    console.log('rowid===>' + rowid + ", cityjson====>" + json.stringify(cityjson));
    if (rowid === key_now || rowid === key_hot || rowid === key_last_visit) {
      return that._renderlistbox(cityjson, rowid);
    }

    return (
      <touchableopacity key={'list_item_' + cityjson.id} style={styles.rowview} onpress={() => {
        that._citynameclick(cityjson)
      }}>
        <view style={styles.rowdata}>
          <text style={styles.rowdatatext}>{cityjson.name}</text>
        </view>
      </touchableopacity>
    )
  }

  _renderlistsectionheader(sectiondata, sectionid) {
    return (
      <view style={styles.sectionview}>
        <text style={styles.sectiontext}>
          {sectiondata}
        </text>
      </view>
    );
  }

  render() {
    return (
      <view style={styles.container}>
        <view style={styles.listcontainner}>
          <listview ref={listview => this._listview = listview}
               contentcontainerstyle={styles.contentcontainer} datasource={this.state.datasource}
               renderrow={this._renderlistrow} rendersectionheader={this._renderlistsectionheader}
               enableemptysections={true} initiallistsize={500}/>
          <view style={styles.letters}>
            {this.state.letters.map((letter, index) => this._renderrightletters(letter, index))}
          </view>
        </view>
        <toast ref="toast" position='top' positionvalue={200} fadeinduration={750} fadeoutduration={1000}
            opacity={0.8}/>
      </view>
    )
  }
}

const styles = stylesheet.create({
  container: {
    // paddingtop: 50,
    flex: 1,
    flexdirection: 'column',
    backgroundcolor: '#f4f4f4',
  },
  listcontainner: {
    height: dimensions.get('window').height,
    marginbottom: 10
  },
  contentcontainer: {
    flexdirection: 'row',
    width: width,
    backgroundcolor: 'white',
    justifycontent: 'flex-start',
    flexwrap: 'wrap'
  },
  letters: {
    position: 'absolute',
    height: height,
    top: 0,
    bottom: 0,
    right: 10,
    backgroundcolor: 'transparent',
    // justifycontent: 'flex-start',
    // alignitems: 'flex-start'
    alignitems: 'center',
    justifycontent: 'center'
  },
  letter: {
    height: height * 4 / 100,
    width: width * 4 / 50,
    justifycontent: 'center',
    alignitems: 'center'
  },
  lettertext: {
    textalign: 'center',
    fontsize: height * 1.1 / 50,
    color: '#e75404'
  },
  sectionview: {
    paddingtop: 5,
    paddingbottom: 5,
    height: 30,
    paddingleft: 10,
    width: width,
    backgroundcolor: '#f4f4f4'
  },
  sectiontext: {
    color: '#e75404',
    fontweight: 'bold'
  },
  rowview: {
    height: rowheight,
    paddingleft: 10,
    paddingright: 10,
    borderbottomcolor: '#f4f4f4',
    borderbottomwidth: 0.5
  },
  rowdata: {
    paddingtop: 10,
    paddingbottom: 2
  },

  rowdatatext: {
    color: 'gray',
    width: width
  },

  rowviewbox: {
    height: rowheight_box,
    width: (width - 30) / 3,
    flexdirection: 'row',
    backgroundcolor: '#ffffff'
  },
  rowdatabox: {
    borderwidth: 1,
    bordercolor: '#dbdbdb',
    margintop: 5,
    marginbottom: 5,
    paddingbottom: 2,
    marginleft: 10,
    marginright: 10,
    flex: 1,
    justifycontent: 'center',
    alignitems: 'center'
  },
  rowdatatextbox: {
    margintop: 5,
    flex: 1,
    height: 20
  }

});
 

然后在头部还需要实现一个搜索框。

searchbox.js

'use strict';
import react, {component} from 'react';
import {
  view,
  textinput,
  stylesheet,
  platform,
} from 'react-native';

export default class searchbox extends component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };

  }

  onendeditingkeyword(vv) {
    console.log(vv);
  }

  onchanegetextkeyword(vv) {
    console.log('onchanegetextkeyword', vv);

    this.setstate({value: vv});
    this.props.onchanegetextkeyword(vv);
  }

  render() {
    return (
      <view style={styles.container}>
        <view style={styles.inputbox}>
          <view style={styles.inputicon}>
          </view>
          <textinput ref="keyword" autocapitalize="none" value={this.props.keyword}
                onchangetext={this.onchanegetextkeyword.bind(this)} returnkeytype="search" maxlength={20}
                style={styles.inputtext} underlinecolorandroid="transparent"
                placeholder={'输入城市名或拼音查询'}/>
        </view>
      </view>
    )
  }
}

const styles = stylesheet.create({
  container: {
    margintop: 5,
    marginbottom: 5,
    backgroundcolor: '#ffffff',
    flexdirection: 'row',
    height: platform.os === 'ios'
      ? 35
      : 45,
    borderbottomwidth: stylesheet.hairlinewidth,
    borderbottomcolor: '#cdcdcd',
    paddingbottom: 5
  },
  inputbox: {
    height: platform.os === 'ios'
      ? 30
      : 40,
    marginleft: 5,
    marginright: 5,
    flex: 1,
    flexdirection: 'row',
    backgroundcolor: '#e6e7e8'
  },
  inputicon: {
    margin: platform.os === 'ios'
      ? 5
      : 10
  },
  inputtext: {
    alignself: 'flex-end',
    margintop: platform.os === 'ios'
      ? 0
      : 0,
    flex: 1,
    height: platform.os === 'ios'
      ? 30
      : 40,
    marginleft: 2,
    marginright: 5,
    fontsize: 12,
    lineheight: 30,
    textalignvertical: 'bottom',
    textdecorationline: 'none'
  }
});

最终效果:

这里写图片描述 

这里写图片描述 

最后是界面的绘制,这里就不多说了,大家可以下载源码自行查看。源码地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网