This is the sixth day of my participation in the August More text Challenge. For details, see:August is more challenging

Touch events

User interaction on mobile apps is largely “touch”. Of course, “touching” can come in a variety of poses: clicking on a button, swiping across a list, or zooming in and out on a map.

onPress

import { Button, Alert } from "react-native"

/ /...
<Button onPress={() = >{Alert.alert('I can't take it anymore.')}>click me</Button>
Copy the code

Effect:

Click on Click Me to pop up the popup box, and the button can also add a color property to change the color of the button

Touchable series of components

The style of this component is fixed. If you want to customize, you need to cooperate with TouchableOpacity and TouchableNativeFeedback to customize.

Usage scenario:

  • You can useTouchableHighlightTo create a button or link. Note that the background of this component darkens when the user’s finger is pressed.
  • It’s also available on AndroidTouchableNativeFeedback, which creates a visual effect similar to ink ripples when the user presses it with a finger.
  • TouchableOpacityLowers the opacity of the button when the user’s finger is pressed, without changing the color of the background.
  • If you want to process the click event without showing any visual feedback, useTouchableWithoutFeedback*.
import React, { Component } from 'react'
import {
    Button,
    Alert,
    Platform,
    StyleSheet,
    Text,
    TouchableHighlight,
    TouchableOpacity,
    TouchableNativeFeedback,
    TouchableWithoutFeedback,
    View
} from 'react-native'

export default class Touchables extends Component {
    _onPressButton() {
        Alert.alert('You tapped the button! ')}_onLongPressButton() {
        Alert.alert('You long-pressed the button! ')}render() {
        return (
            <View style={styles.container}>
                <Button
                    onPress={()= >}} title="click me!" /><TouchableHighlight
                    onPress={this._onPressButton}
                    underlayColor="white"
                >
                    <View style={styles.button}>
                        <Text style={styles.buttonText}>
                            TouchableHighlight
                        </Text>
                    </View>
                </TouchableHighlight>
                <TouchableOpacity onPress={this._onPressButton}>
                    <View style={styles.button}>
                        <Text style={styles.buttonText}>TouchableOpacity</Text>
                    </View>
                </TouchableOpacity>
                <TouchableNativeFeedback
                    onPress={this._onPressButton}
                    background={
                        Platform.OS= = ='android'
                            ? TouchableNativeFeedback.SelectableBackground(a):"'} >
                    <View style={styles.button}>
                        <Text style={styles.buttonText}>
                            TouchableNativeFeedback
                        </Text>
                    </View>
                </TouchableNativeFeedback>
                <TouchableWithoutFeedback onPress={this._onPressButton}>
                    <View style={styles.button}>
                        <Text style={styles.buttonText}>
                            TouchableWithoutFeedback
                        </Text>
                    </View>
                </TouchableWithoutFeedback>
                <TouchableHighlight
                    onPress={this._onPressButton}
                    onLongPress={this._onLongPressButton}
                    underlayColor="white"
                >
                    <View style={styles.button}>
                        <Text style={styles.buttonText}>
                            Touchable with Long Press
                        </Text>
                    </View>
                </TouchableHighlight>
            </View>)}}const styles = StyleSheet.create({
    container: {
        paddingTop: 60.alignItems: 'center'
    },
    button: {
        marginBottom: 30.width: 260.alignItems: 'center'.backgroundColor: '#2196F3'
    },
    buttonText: {
        textAlign: 'center'.padding: 20.color: 'white'}})Copy the code

Effect:

The timer

Timers are a very important part of an application. React Native implements a timer that is consistent with the browser.

API

  • setTimeout, clearTimeout
  • setInterval, clearInterval
  • setImmediate, clearImmediate
  • requestAnimationFrame, cancelAnimationFrame

Unlike the requestAnimationFrame(fn), which is executed once after each frame refresh, the setTimeout(fn, 0) is executed as quickly as possible (possibly more than 1000 times per second on the iPhone5S).

SetImmediate executes at the end of the current JavaScript execution block, just before the bulk response data is sent to the native. Note that if you execute setImmediate in a callback to setImmediate, it executes immediately after, without waiting for native code before calling.

Promise’s implementation uses setImmediate to perform asynchronous calls.

InteractionManager

Some of the more time-consuming work can be scheduled until all interactions or animations are complete. This keeps JavaScript animations running smoothly.

Contrast with the timer method:

  • requestAnimationFrame(): Executes code that controls the view animation for a period of time
  • setImmediate/setTimeout/setInterval(): Executes the code later. Note that this may delay the animation that is currently in progress.
  • runAfterInteractions(): Executes the code at a later time without delaying the current animation.

The touch handling system recognizes one or more ongoing touches as’ interactions’ and delays the runAfterInteractions() callback until all touches have been completed or canceled.

InteractionManager also allows applications to register an animation, create an interaction “handle” at the beginning of the animation, and then clear it at the end.

const handle = InteractionManager.createInteractionHandle()
// Execute the animation... (Tasks in 'runAfterInteractions' are now queued)
// After the animation is finished
InteractionManager.clearInteractionHandle(handle)
// After all handles have been cleared, the tasks in the queue are now executed in order
Copy the code

Clearing the timer after the component is unloaded can avoid some fatal errors; For example, the system fails to respond for a long time.

Gesture response system

TouchableHighlight and Touchable series components

The response system can be complicated to use. So we’ve provided an abstract implementation of Touchable for “Touchable” components. This implementation takes advantage of a responsive system that allows you to simply configure touch handling in a declarative manner. If you want to make a button or web link, use TouchableHighlight.

The lifecycle of the responder

A View can become a responder to a touch event as long as it implements the correct negotiation method. There are two ways to “ask” a View if it wants to be a responder:

  • View.props.onStartShouldSetResponder: (evt) => true,– Are you willing to be a responder when the user starts to touch (the moment when the finger touches the screen)?
  • View.props.onMoveShouldSetResponder: (evt) => true,– If the View is not the responder, ask again at each touch point that begins to move (without stopping or leaving the screen) : would you like to respond to touch interaction?

If the View returns true and starts trying to be a responder, one of the following events is triggered:

  • View.props.onResponderGrant: (evt) => {}– View is now ready to respond to touch events. This is also the time to highlight, to let the user know where he is.
  • View.props.onResponderReject: (evt) => {}– The responder is now “someone else” and will not “delegate” for the time being, please make other arrangements.

If the View has already started responding to touch events, the following handlers are called:

  • View.props.onResponderMove: (evt) => {}– When the user is moving their finger across the screen (without stopping or leaving the screen).
  • View.props.onResponderRelease: (evt) => {}– Triggered when the touch operation is over, such as “touchUp” (the finger is lifted away from the screen).
  • View.props.onResponderTerminationRequest: (evt) => true– Is the View “delegated” when another component requests to take over the responder? Returning true releases the responder’s power.
  • View.props.onResponderTerminate: (evt) => {}– Responder power has been surrendered. This may be due to other views passing throughonResponderTerminationRequestRequested, or forced by the operating system (such as the control center or notification center on iOS).

Evt is a composite event that contains the following structure:

  • nativeEvent
    • changedTouches– An array of all touch events that have changed since the last event (i.e. all touch points that have moved since the last event)
    • identifier– ID of the touch point
    • locationX– The abscissa of the touch point relative to the current element
    • locationY– The ordinate of the touch point relative to the current element
    • pageX– The abscissa of the touch point relative to the root element
    • pageY– The ordinate of the touch point relative to the root element
    • target– ID of the element where the touch point resides
    • timestamp– A timestamp of the touch event that can be used to calculate the movement speed
    • touches– The collection of all touch points on the current screen

Capture ShouldSet event handling

OnStartShouldSetResponder and onMoveShouldSetResponder is invoked in the form of a bubble, namely the deepest nested nodes are the first to call. This means that when multiple views return true in a *ShouldSetResponder at the same time, the View at the bottom of the list will have the first “override”. In most cases this is fine because it ensures that all controls and buttons are available.

But sometimes a parent View will want to be the responder first. We can use the “capture period” to address this need. Response system before starting at the bottom of the component bubbling, will first perform a “capture” period, during this period will trigger on * ShouldSetResponderCapture series of events. Therefore, if a parent View to stop at the start of touch child components become a responder, it should handle onStartShouldSetResponderCapture events and return true value.

  • View.props.onStartShouldSetResponderCapture: (evt) => true,
  • View.props.onMoveShouldSetResponderCapture: (evt) => true,

The network connection

React Native provides a Web standards-compliant Fetch API for developers to access the web. You can also use third-party libraries (such as Axios)

Fetch

If you are not familiar with Fetch, you can first look at the MDN documentation

Use Fetch to implement a network request; Request: click a button to send the request, and the data will be displayed on the page;

You can start with some free online apis, or build your own; Recommend using JSONPlacehodler, an online, free, out-of-the-way RESTful interface.

The code is as follows:

import React, { Component } from 'react'
import {
    View,
    Text,
    Button,
    StyleSheet,
    FlatList,
    SafeAreaView,
    ActivityIndicator
} from 'react-native'
import Loading from '.. /.. /components/loading/Loading'

// Create the style
const style = StyleSheet.create({
    text: {
        fontSize: 24.color: 'skyblue'
    },
    button: {
        width: '40%'.height: 30.borderWidth: 1.borderColor: 'red'
    },
    item: {
        fontSize: 26.fontWeight: 500 ` `.color: '# 333'.marginTop: 10
    },
    flatList: {
        borderColor: 'red'.borderWidth: 1.width: '95%'.height: '100%'.marginLeft: 'auto'.marginRight: 'auto'}})export default class LinFetch extends Component {
    state = {
        loading: false.count: 0.data: []}// Get the data
    async handleFetch() {
        const result = await fetch(`https://jsonplaceholder.typicode.com/todos`)
        const data = await result.json()
        this.setState({ data, loading: false})}// Pull down load
    handleOnRefresh() {
        this.setState({ loading: true })
        this.handleFetch()
    }

    // Pull up load
    handleOnEndReached() {
        const { loading } = this.state
        this.setState({ loading: !loading })
    }

    render() {
        const { data, loading } = this.state
        return (
            <View style={{ flex: 1.width: '100% ',height: '100'}} % >
                <Text style={style.text}>fetch request</Text>
                <Button
                    style={style.button}
                    onPress={()= > this.handleFetch()}
                    title="request"
                />
                <SafeAreaView
                    style={{ flex: 1.width: '100% ',height: '100'}} % >
                    <FlatList
                        style={style.flatList}
                        data={data}
                        refreshing={loading}
                        onRefresh={()= > this.handleOnRefresh()}
                        onEndReached={() => this.handleOnEndReached()}
                        keyExtractor={item => item.id}
                        renderItem={({ item }) => (
                            <Text style={style.item}>{item.title}</Text>
                        )}
                        ListFooterComponent={loading && <Loading />} / ></SafeAreaView>
            </View>)}}Copy the code

When configuring refreshing, you must set the refreshing property. Otherwise, an error message is displayed, as shown in the following figure

The final renderings are as follows:

Axios

In fact, these methods are the same as when developing web applications. If you are not familiar with Axios, you can go to the Axios website to get familiar with it.

  • The installation

    $ npm i axios
    or
    $ yarn add axios
    Copy the code
  • Request the image in the JSONPlaceholder

    state = {
        albumId: 1.data: [].isLoading: false,}async componentDidMount() {
        await this.handleGetData()
    }
    
    async handleGetData() {
        const { albumId, data } = this.state
        const result = await axios.get(
        		`https://jsonplaceholder.typicode.com/albums/${albumId}/photos`.)this.setState({
            data: [data, ...result.data],
            isLoading: false})}Copy the code
  • View to show

    <FlatList
        data={data}
        renderItem={({ item }) = > (
            <>
                <Image
                    style={styles.stretch}
                    source={{ uri: item.url }}
                    resizeMode="contain"
                />
                <Text>{item.title}</Text>
            </>
        )}
        keyExtractor={item= > item.id}
        onEndReached={() = > this.handleOnEndReached()}
    />
    Copy the code

The complete code is as follows:

import React, { Component } from 'react'
import axios from 'axios'
import { View, Text, Image, StyleSheet, FlatList } from 'react-native'

const styles = StyleSheet.create({
    text: {
        fontSize: 24.fontWeight: '600'
    },
    stretch: {
        width: '100%'.height: 500.resizeMode: 'stretch'}})class LinAxios extends Component {
    state = {
        albumId: 1.data: [].isLoading: false
    }

    componentDidMount() {
        this.handleGetData()
    }

    // Get the data
    async handleGetData() {
        const { albumId, data } = this.state
        const result = await axios.get(
            `https://jsonplaceholder.typicode.com/albums/${albumId}/photos`
        )
        console.log('albmId:', albumId)
        this.setState({ data: [data, ...result.data], isLoading: false})}// Pull up load
    async handleOnEndReached() {
        if (this.state.isLoading) return
        await this.setState({
            albumId: this.state.albumId + 1.isLoading: true
        })
        this.handleGetData()
    }

    render() {
        const { data } = this.state

        return (
            <View>
                <Text style={styles.text}>axios request</Text>
                <FlatList
                    data={data}
                    renderItem={({ item}) = > (
                        <>
                            <Image
                                style={styles.stretch}
                                source={{ uri: item.url }}
                                resizeMode="contain"
                            />
                            <Text>{item.title}</Text>
                        </>
                    )}
                    keyExtractor={item => item.id}
                    onEndReached={() => this.handleOnEndReached()}
                />
            </View>
        )
    }
}

export default LinAxios
Copy the code