FRONTEND/ReactNative

[전기차 충전소 찾기 앱/ReactNative] 충전소 리스트 보여주기

죠으닝 2024. 2. 15. 09:56

 

GlobalApi.js에서 shortFormattedAddress추가하기

      "places.evChargeOptions",
      "places.shortFormattedAddress",
      "places.photos",

 

 

PlaceListView.jsx 생성

 

import { View, Text } from 'react-native';
import React from 'react';

export default function PlaceListView({placeList}) {
  return (
    <View>
      <Text>PlaceListView</Text>
    </View>
  );
}

 

 

HomeScreen.jsx

검색결과로 palceList 스테이트를 만듬

 

  const [placeList, setPlaceList] = useState([]);

 

    //구글맵 서버에서 받은 resp.data.places 데이터를 placeList에 저장
    GlobalApi.NewNearByPlace(data).then((resp) => {
      // console.log(JSON.stringify(resp.data));
      setPlaceList(resp.data?.places);
    });

 

화면에 표시할 css를 수정한다.

    <View>
      <View style={styles.headerContainer}>
        <Header />
        <SearchBar searchedLocation={(location) => console.log(location)} />
      </View>
      <AppMapView />
      <View style={styles.placeListContainer}>
        {placeList && <PlaceListView placeList={placeList} />}
      </View>
    </View>

 

const styles = StyleSheet.create({
  headerContainer: {
    position: "absolute",
    padding: 20,
    width: "90%",
    zIndex: 10,
  },
  placeListContainer: {
    position: "absolute",
    bottom: 0,
    zIndex: 10,
    width: "100%",
  },
});

 

 

 

 PlaceListView.jsx

      <FlatList
        data={placeList}
        renderItem={({ item, index }) => (
          <View key={index}>
            <Text>{item?.displayName?.text}</Text>
          </View>
        )}
      />

 

충전소 리스트가 아래와 같이 뜬다.

 

그리고 위의 PlaceListView 안의  한개의 아이템을 따로 컴포넌트를 만들어서 분리한다.

 

PlaceListView.jsx

      <FlatList
        data={placeList}
        renderItem={({ item, index }) => (
          <View key={index}>
            <PlaceItem place={item} />
          </View>
        )}
      />

 

 

PlaceItem.jsx

 

export default function PlaceItem({ place }) {
  return (
    <View>
      <Text>{place.displayName?.text}</Text>
    </View>
  );
}

 

 

 

위 사진을 다운로드받아  assets-> images-> station으로 저장한다.

 

import { Dimensions, Image, Text, View } from "react-native";

export default function PlaceItem({ place }) {
  return (
    <View
      style={{
        width: Dimensions.get("screen").width * 0.9,
        backgroundColor: "white",
        margin: 5,
        borderRadius: 10,
      }}
    >
      <Image
        source={require("./../../../assets/images/station.png")}
        style={{ width: "100%", borderRadius: 10, height: 130 }}
      />
      <View style={{ padding: 15 }}>
        <Text style={{ fontSize: 17, fontFamily: "Pretend-Medium" }}>
          {place.displayName?.text}
        </Text>
        <Text style={{ color: "gray", fontSize: 13 }}>
          {place?.shortFormattedAddress}
        </Text>
        <Text style={{ fontFamily: "Pretend-Medium" }}>
          충전기기수 : {place?.evChargeOptions?.connectorCount ?? "모름"}
        </Text>
      </View>
    </View>
  );
}

 

 

이미지를 가로로 스크롤 할려면 PlaceListView의 FlatList 에서 horizontal로 설정

 

import { View, Text, FlatList } from "react-native";
import React from "react";
import PlaceItem from "./PlaceItem";

export default function PlaceListView({ placeList }) {
  return (
    <View>
      <FlatList
        data={placeList}
        horizontal={true}
        showsHorizontalScrollIndicator={false}
        renderItem={({ item, index }) => (
          <View key={index}>
            <PlaceItem place={item} />
          </View>
        )}
      />
    </View>
  );
}

 

 

 

 

현재는 실제 이미지를 가져오지않아서 station이미지를 임시로 넣어놨는데 

 

실제 충전소 이미지를 가져올 수 있다.

 

https://developers.google.com/maps/documentation/places/web-service/place-photos?hl=ko

 

장소 사진 (신규)  |  Places API  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 장소 사진 (신규) 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 장소 사진 (신규) 서비스

developers.google.com

export default function PlaceItem({ place }) {
  //전체주소 https://places.googleapis.com/v1/NAME/media?key=API_KEY&PARAMETERS
  const PLACE_PHOTO_BASE_URL = 'https://places.googleapis.com/v1/';
  const apiKey = process.env.EXPO_PUBLIC_API_KEY;

 

 

이미지의 source를 수정한다.

      <Image
        source={
          place?.photos
            ? {
                uri:
                  PLACE_PHOTO_BASE_URL +
                  place?.photos[0].name +
                  "/media?key=" +
                  apiKey +
                  "&maxHeightPx=800&maxWidthPx=1200",
              }
            : require("./../../../assets/images/station.png")
        }
          style={{ width: "100%", borderRadius: 10, height: 150 }}
      />

 

 

 

이어서 폰트오썸 아이콘을 추가한다.

 

 

import FontAwesome from "@expo/vector-icons/FontAwesome";

 

        <View
          style={{
            padding: 12,
            backgroundColor: "#C7EA46",
            borderRadius: 6,
            paddingHorizontal: "40%",
            flexDirection: "row",
          }}
        >
          <FontAwesome name="location-arrow" size={24} color="white" />
          <Text style={{ color: "white", marginLeft: 10 }}>이동</Text>
        </View>
      </View>
    </View>
  );
}

 

 

 

 

지도에 충전소 위치를 마커로 표시한다.

 

HomeScreen.jsx

 

기존에 AppMapView를

placeList가 있을 경우에 출력하도록 한다.

 

AppMapView.jsx


export default function AppMapView({ placeList }) {
  </Marker>
        {placeList &&
          placeList.map((item, index) => {
            return (
              <Marker
                key={index}
                coordinate={{
                  latitude: item.location?.latitude,
                  longitude: item.location?.longitude,
                }}
              ></Marker>
            );
          })}
      </MapView>
    </View>
  );
}

 

 

 

마커의 이미지를 다른 이미지로  변경해보겠다.

마커 이미지를 64px이하로 다운받아서 images 폴더에 넣고 마커태그 안에

아래 이미지 태그를 넣는다. 이때 스타일로 간단한 사이즈를 맞춘다.

 

https://www.flaticon.com/kr/search?author_id=313&style_id=1222&type=standard&word=-

 

Flaticon

Flaticon

www.flaticon.com

 

marker라는 이름으로 저장했다

 

AppMapView의 marker안에 넣는다

        {placeList &&
          placeList.map((item, index) => {
            return (
              <Marker
                key={index}
                coordinate={{
                  latitude: item.location?.latitude,
                  longitude: item.location?.longitude,
                }}
              >
                <Image
                  source={require("./../../../assets/images/marker.png")}
                  style={{ width: 25, height: 30 }}
                />
              </Marker>
            );
          })}

 

 

 

PlaceListView.jsx

 

FlatList에 pagingEnabled 속성을 넣어서

한개의 아이템이 한페이지에 나오도록 수정한다.

  const flatListRef = useRef(null);

  const getItemLayout = (_, index) => ({
    length: Dimensions.get("window").width,
    offset: Dimensions.get("window").width * index,
    index,
  });
  const scrollToIndex = (index) => {
    flatListRef.current?.scrollToIndex({ animated: true, index });
  };

  useEffect(() => {
    // scrollToIndex(0);
  }, []);

 

  return (
    <View
      style={{
        width: Dimensions.get("screen").width * 0.9,
        backgroundColor: "white",
        margin: Dimensions.get("screen").width * 0.05,
        borderRadius: 10,
      }}
    >
      <FlatList
        data={placeList}
        horizontal={true}
        pagingEnabled
        ref={flatListRef}
        getItemLayout={getItemLayout}
        showsHorizontalScrollIndicator={false}
        renderItem={({ item, index }) => (
          <View key={index}>
            <PlaceItem place={item} />
          </View>
        )}
      />
    </View>
  );
}

 

 

마커를 클릭하면 번호가 출력되게

 

 

AppMapView.jsx 

 

        {placeList &&
          placeList.map((item, index) => {
            return (
              <Marker
                onPress={() => console.log("마커인덱스", index)}
                key={index}
                coordinate={{
                  latitude: item.location?.latitude,
                  longitude: item.location?.longitude,
                }}
              >

 

 

마커의 번호를 컨텍스트로 공유하기 위해 

Context를 추가한다.

import { createContext } from "react";

export const SelectMarkerContext = createContext(null);

 

 

HomeScreen

import { SelectMarkerContext } from '../../Context/SelectMarkerContext';

...
  const [selectedMarker, setSelectedMarker] = useState(null);
...

  return (
    <SelectMarkerContext.Provider value={{ selectedMarker, setSelectedMarker }}>
    ... 원래있던 내용 감싸기
    </SelectMarkerContext.Provider>

 

 

AppMapView

onpress 하면 콘솔로 출력되던 코드를 아래와 같이 수정하여 

HomeScreen에서 setState에 상태 저장하도록 한다.

 

   <Marker
                onPress={() => setSelectedMarker(index)}

 

 

 

PlaceListView.jsx

 

만들어놓은 SelectMarkerContext를 가져와서 

 

상태 저장된 마커가 null이 아니면 그 선택한 index 로 스크롤한다.

 

import React, { useContext, useEffect, useRef } from 'react';
...
import { SelectMarkerContext } from '../../Context/SelectMarkerContext';
...


  const { selectedMarker } = useContext(SelectMarkerContext);

  useEffect(() => {
    selectedMarker !== null && scrollToIndex(selectedMarker);
  }, [selectedMarker]);