I made an album that looks sort of like this:
<ScrollView>
<Image1
<Text1>
smth
</Text1> />
<Image2
<Text2>
smthElse
</Text2> />' ... and so on 20 times.
I have 5 albums, which I access with tabs on top.
The problem is that sometimes when I switch between the tabs, some images are blank and don't load. The images are all required like this:
source={require('../assets/Image1.png')}
I want them to be required from the app and not via uri.
I thought the problem is because of the memory and on weaker phones it's possible that they don't always load, because I use a ScrollView and it loads all the images at once. I read that a ListView quite solve my problem, but I can't figure out how to make a ListView that renders 20 specific images with a specific text for each one.
If anyone can give me some clues it would be much appreciated.
Thank you !
In order to use <ListView> You can require all the images on your .js file. On the top of your .js file you can do is
index.js
import React, { Component } from 'react';
import {Listview} from 'react-native'; //Import your other imports ofcourse
// Require all your images here
const image1 = require('../assets/Image1.png')
const image2 = require('../assets/Image2.png')
//.. And so on
export default class ClassName extends Component{
//...
}
Next is create an array object and add it to your constructor on your index.js like so
index.js
import React, { Component } from 'react';
import {Listview} from 'react-native'; //Import your other imports ofcourse
// Require all your images here
const image1 = require('../assets/Image1.png')
const image2 = require('../assets/Image2.png')
var data = [{title:"You image title", image: image1}, {title:"Your Image title",image: image2}]
export default class ClassName extends Component{
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(data),
};
}
}
Next Create a Row.js
Row.js
import React from 'react'
import { StyleSheet, Dimensions, Platform, Image, View, Text } from 'react-native';
const Row = (props) => (
<View style={{flex:1, flexDirection: 'row'}}> //Don't forget this
<Image source={props.image}>
<Text>{props.title}</Text>
</Image>
</View>
)
export default Row
Lastly Import your Row.js file on your index.js and add the <ListView> on your render()
index.js
import React, { Component } from 'react';
import {View,Listview} from 'react-native'; //Import your other imports ofcourse
import Row from './Row'; //If it's on the same folder
// Require all your images here
const image1 = require('../assets/Image1.png')
const image2 = require('../assets/Image2.png')
var data = [{title:"You image title", image: image1}, {title:"Your Image title",image: image2}]
export default class ClassName extends Component{
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(data),
};
}
render(){
<View>
<ListView
style={{flex:1}} //Don't forget this too
dataSource={this.state.dataSource}
renderRow={(data) => <Row {...data} />}
/>
</View>
}
}
Hope this helps you. Cheers!
In response to your question how to use a ListView to render 20 images. We are using a SectionList for rendering multiple photos. I will share the snippets below to get you started.
SectionList
<SectionList
contentContainerStyle={styles.sectionListContainer}
renderItem={this.renderItem}
renderSectionHeader={this.renderHeader}
sections={this.state.ImageData}
/>
this.renderItem
renderItem(data) {
return (
<View key={data.item.key} style={{ flex: 1 }}>
<View style={{ flex: 1, flexDirection: "row", flexWrap: "wrap" }}>
<Image
style={styles.foodPhoto}
source={{ uri: `data:image/jpg;base64,${data.item.photo}`}}
/>
</View>
</View>
)
}
To format the this.state.ImageData it took us a bit of fiddling to get the formatting/schema right for the SectionList. The format itself is documented at RN documentation. Hope that this helps!
Related
As the title says, I'm trying to send data from a Flatlist component and send it to another component (click on toy in list, opens up the toy details). I've tried numerous alternative approaches, and can't figure out what isn't working. This is the closest I've come to it working, where there's no error messages. Alternatively would prefer to use useState to pass data
Passing the data through my flatlist component:
import { StyleSheet, Text, View, FlatList, TouchableOpacity } from 'react-native'
import React, {useState} from 'react'
import Toy from './Database'
import ToyCard from './ToyCard'
const FLToyCard = ({navigation}) => {
const headerComp = () => {
return(
<View style={{alignSelf: 'center'}}>
<Text style={{fontSize: 25, padding: 10}}>All Toys For Sale</Text>
</View>
)
}
return(
<View>
<FlatList
data={Toy}
renderItem={({item})=>{const {name, id, image, price, desc, seller, type, longDesc} = item; return(<View style={{flex:1}}><ToyCard name={name} image={image} price={price} desc={desc} seller={seller} id={id} type={type} longDesc={longDesc} onPress={()=>navigation.navigate('ToyDetails', {name, id, image, price, seller, desc, longDesc, type})}/></View>)}}
keyExtractor={(item)=>item.id}
numColumns={2}
ListHeaderComponent={headerComp}
/>
</View>
)
}
export default FLToyCard
Retrieving the data (would prefer it being retreived into the SlugFormat component, but its not a navigational page so just in case, I added one of the props to this page to see if its retrieving the data):
import { StyleSheet, Text, View } from 'react-native'
import React from 'react'
import SlugFormat from '../Components/SlugFormat'
const ToyDetails = ({navigation, name}) => {
return (
<View>
<SlugFormat navigation={navigation}/>
<Text>{name}</Text>
</View>
)
}
export default ToyDetails
This is based off the help someone tried to give me on my last post, you can see it see here if necessary.
when you go to any page using navigation that time this props is received inside route.params
const ToyDetails = ({route,navigation}) => {
const {name } =route.params
return (
<View>
<SlugFormat navigation={navigation}/>
<Text>{name}</Text>
</View>
)
}
I am trying to randomly display images from a local file that have been stored in an array in a Card component which is then rendered in the App, the root of the application.
Here is the Images file containing an array of images in the same directory.
const woolyImages = [
require('images/wooly1'),
require('images/wooly2'),
require('images/wooly3'),
require('images/wooly4'),
require('images/wooly5'),
];
export default woolyImages;
Here is the Card component where I attempt to pick a random image from the array and display it using the Image component via 'source'.
import React from 'react';
import {Image, View, StyleSheet} from 'react-native';
import woolyImages from '../images/Images';
function Card() {
const randomImage =
woolyImages[Math.floor(Math.random() * woolyImages.length)];
console.log(randomImage);
return (
<View style={styles.cardContainer}>
<Image source={randomImage} />
</View>
);
}
const styles = StyleSheet.create({
cardContainer: {
width: '50%',
height: '35%',
backgroundColor: 'pink',
},
});
export default Card;
(I inserted the pink background so I could be sure it was rendering in the view. It is.)
Finally, here is the root of the application where I render the card.
import React from 'react';
import {View, StyleSheet} from 'react-native';
import Header from './components/Header';
import Card from './components/Card';
import GenerateWoolyButton from './components/GenerateWoolyButton';
function App() {
return (
<View style={styles.container}>
<Header />
<Card />
<GenerateWoolyButton style={styles.button} />
</View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: 'flex-start',
alignItems: 'center',
flex: 1,
},
});
export default App;
I am getting the below error.
Can anyone please tell me how to display a randomly generated picture in the Card component and display it in the root of my application? Thank you.
You need to add width & height styles to Image of your Card Component. Such as,
<Image source={randomImage} style={{height: 200, width: 200}} />
Hope this will helps you. Feel free for doubts.
I am trying to load a remote placeholder image. The first image is a local image and loads properly, but not the second image. I am testing this on an android device so I don't think https would cause problems.
Any hints to what I might be doing wrong?
import React, { Component } from 'react';
import { Text, View, Dimensions, TouchableOpacity, Image, ToastAndroid, Animated } from 'react-native';
import styles from "./styles";
class Story extends Component {
constructor(props){
super(props);
this.state={};
}
render() {
return (
<View style={{position:'relative'}}>
<Image source={require('app/assets/images/campus.png')} style={styles.container}></Image>
<Image source={{ uri: 'https://place-hold.it/100x200/fdd.png' }} style={styles.character1}></Image>
</View>
);
}
}
export default Story;
// firstly set isImageload false
then implement image like this
var image= this.state.isImageload ? require('place holder image') : {uri: ImageUrl};
return(
<Image
source={immage}
onLoadStart={() => this.setState({isImageload : true})}
onLoad={() => this.setState({isImageload : false})}
/>
)
I removed one pair bracket from the Image tag and the code become:
<Image source={ uri: 'https://place-hold.it/100x200/fdd.png' } style={styles.character1}></Image>
Please try it to see whether if works.
Is it possible to tell a ScrollView to scroll to a specific position when we just navigated to the current screen via StackNavigator?
I have two screens; Menu and Items. The Menu is a list of Buttons, one for each item. The Items screen contain a Carousel built using ScrollView with the picture and detailed description of each Item.
When I click on a button in the Menu screen, I want to navigate to the Items screen, and automatically scroll to the Item that the button represent.
I read that you can pass in parameters when using the StackNavigator like so: but I don't know how to read out that parameter in my Items screen.
navigate('Items', { id: '1' })
So is this something that is possible in React Native and how do I do it? Or perhaps I'm using the wrong navigator?
Here's a dumbed down version of my two screens:
App.js:
const SimpleApp = StackNavigator({
Menu: { screen: MenuScreen},
Items: { screen: ItemScreen }
}
);
export default class App extends React.Component {
render() {
return <SimpleApp />;
}
}
Menu.js
export default class Menu extends React.Component {
constructor(props){
super(props)
this.seeDetail = this.seeDetail.bind(this)
}
seeDetail(){
const { navigate } = this.props.navigation;
navigate('Items')
}
render(){
<Button onPress={this.seeDetail} title='1'/>
<Button onPress={this.seeDetail} title='2'/>
}
}
Items.js
export default class Items extends React.Component {
render(){
let scrollItems = [] //Somecode that generates and array of items
return (
<View>
<View style={styles.scrollViewContainer}>
<ScrollView
horizontal
pagingEnabled
ref={(ref) => this.myScroll = ref}>
{scrollItems}
</ScrollView>
</View>
</View>
)
}
}
P.S I am specifically targeting Android at the moment, but ideally there could be a cross-platform solution.
I read that you can pass in parameters when using the StackNavigator like so: but I don't know how to read out that parameter in my Items screen.
That is achieved by accessing this.props.navigation.state.params inside your child component.
I think the best time to call scrollTo on your scrollview reference is when it first gets assigned. You're already giving it a reference and running a callback function - I would just tweak it so that it also calls scrollTo at the same time:
export default class Items extends React.Component {
render(){
let scrollItems = [] //Somecode that generates and array of items
const {id} = this.props.navigation.state.params;
return (
<View>
<View style={styles.scrollViewContainer}>
<ScrollView
horizontal
pagingEnabled
ref={(ref) => {
this.myScroll = ref
this.myScroll.scrollTo() // !!
}>
{scrollItems}
</ScrollView>
</View>
</View>
)
}
}
And this is why I use FlatLists or SectionLists (which inherit from VirtualizedList) instead of ScrollViews. VirtualizedList has a scrollToIndex function which is much more intuitive. ScrollView's scrollTo expects x and y parameters meaning that you would have to calculate the exact spot to scroll to - multiplying width of each scroll item by the index of the item you're scrolling to. And if there is padding involved for each item it becomes more of a pain.
Here is an example of scroll to the props with id.
import React, { Component } from 'react';
import { StyleSheet, Text, View, ScrollView, TouchableOpacity, Dimensions, Alert, findNodeHandle, Image } from 'react-native';
class MyCustomScrollToElement extends Component {
constructor(props) {
super(props)
this.state = {
}
this._nodes = new Map();
}
componentDidMount() {
const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
data.filter((el, idx) => {
if(el===this.props.id){
this.scrollToElement(idx);
}
})
}
scrollToElement =(indexOf)=>{
const node = this._nodes.get(indexOf);
const position = findNodeHandle(node);
this.myScroll.scrollTo({ x: 0, y: position, animated: true });
}
render(){
const data = ['First Element', 'Second Element', 'Third Element', 'Fourth Element', 'Fifth Element' ];
return (
<View style={styles.container}>
<ScrollView ref={(ref) => this.myScroll = ref} style={[styles.container, {flex:0.9}]} keyboardShouldPersistTaps={'handled'}>
<View style={styles.container}>
{data.map((elm, idx) => <View ref={ref => this._nodes.set(idx, ref)} style={{styles.element}}><Text>{elm}</Text></View>)}
</View>
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexGrow: 1,
backgroundColor:"#d7eff9"
},
element:{
width: 200,
height: 200,
backgroundColor: 'red'
}
});
export default MyCustomScrollToElement;
Yes, this is possible by utilising the scrollTo method - see the docs. You can call this method in componentDidMount. You just need a ref to call it like: this.myScroll.scrollTo(...). Note that if you have an array of items which are all of the same type, you should use FlatList instead.
For iOS - the best way to use ScrollView's contentOffset property. In this way it will be initially rendered in a right position. Using scrollTo will add additional excess render after the first one.
For Android - there is no other option rather then scrollTo
I use react-native-video-player and I have difficulties with adjusting size of video player, it's too big and doesn't fit into screen and control buttons doesn't fit into available space.
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import VideoPlayer from 'react-native-video-player';
export default class VideoPage extends Component {
render() {
return (
<View>
<VideoPlayer
video={{ uri: 'file:///storage/emulated/0/Download/sample_video.mp4' }}
videoWidth={1280}
videoHeight={720}
/>
</View>
);
}
}
Here is what I have now, as you can see this is NOT OK.
I need some help with how to make it smaller. I tried to specify width & height as you can see, but it just doesn't change anything it stays as it is. Maybe there is something I don't know ?
Thank you in advance for any help or suggestions.
set size to your view not your videoplayer and do it by styling.
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import VideoPlayer from 'react-native-video-player';
export default class VideoPage extends Component {
render() {
return (
<View style={{width: 1280, height: 720}}>
<VideoPlayer
video={{ uri: 'file:///storage/emulated/0/Download/sample_video.mp4' }}
/>
</View>
);
}
}