React-Native带你一步一步实现侧滑删除(二)

上一节我们已经简单的实现了侧滑删除,
这里写图片描述

我们最后还留了一个小小的功能,那就是当手指抬起的时候,我们需要选择打开或者关闭侧滑功能,我们分几种情况考虑:

大的条件分两种:
一、侧滑已经打开
1、抬起手指的时候,如果偏移的距离>=删除按钮距离(打开)

1、抬起手指的时候,如果偏移的距离<删除按钮距离(关闭)

二、侧滑没有打开
1、抬起手指的时候,如果偏移的距离>=删除按钮距离*偏移量(打开)

1、抬起手指的时候,如果偏移的距离<删除按钮距离*偏移量(关闭)

有了逻辑之后我们的代码就很简单了:

 /**
     * 结束事件的时候回调
     * @param event
     * @param gestureState
     * @private
     */
    _handlePanResponderEnd(event: Object, gestureState: Object): void {
        if(this._isOpen){
            if (Math.abs(this.state.currentLeft._value) >= 100) {
                this._animateToOpenPosition();
            } else {
                this._animateToClosedPosition();
            }
        }else{
            if (Math.abs(this.state.currentLeft._value) >= 100 / 3) {
                this._animateToOpenPosition();
            } else {
                this._animateToClosedPosition();
            }
        }
        this._previousLeft = null;
    }

最后是SwipeRow.js的全部代码:

/**
 * @author YASIN
 * @version [React-Native Ocj V01, 2018/3/13]
 * @date 17/2/23
 * @description SwipeRow
 */
import React, {
    Component,
} from 'react';
import PropTypes from 'prop-types';
import {
    Animated,
    PanResponder,
    Platform,
    StyleSheet,
    TouchableOpacity,
    ViewPropTypes,
    View,
    Text
} from 'react-native';

export default class SwipeRow extends Component {
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this._panResponder = PanResponder.create({
            onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),
            onPanResponderGrant: this._handlePanResponderGrant.bind(this),
            onPanResponderMove: this._handlePanResponderMove.bind(this),
            onPanResponderRelease: this._handlePanResponderEnd.bind(this),
            onPanResponderTerminate: this._handlePanResponderEnd.bind(this),
            onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平台的事件处理,默认是禁用的,全部使用 JS 中的事件处理,注意此函数目前只能在 Android 平台上使用
        });
        //上一次滑动最后的left偏移量
        this._previousLeft = 0;
        //left偏移动画
        this.state = {
            currentLeft: new Animated.Value(this._previousLeft),
        };
        this._isOpen = false;
    }

    render() {
        return (
            <View style={[styles.swipeContainer, this.props.style]}>
                <View style={styles.swipeActions}>
                    {this.props.children[0]}
                </View>
                {this.renderRowContent()}
            </View>
        );
    }

    renderRowContent() {
        return (
            <Animated.View
                {...this._panResponder.panHandlers}
                style={{
                    transform: [
                        {translateX: this.state.currentLeft}
                    ]
                }}
            >
                {this.props.children[1]}
            </Animated.View>
        );
    }

    /**
     * 是否需要成为move事件响应者,返回true直接走onPanResponderMove
     * @param event
     * @param gestureState
     * @returns {boolean}
     * @private
     */
    _handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {
        //当垂直滑动的距离<10 水平滑动的距离>10的时候才让捕获事件
        console.log('_handleMoveShouldSetPanResponderCapture');
        return Math.abs(gestureState.dy) < 10 && Math.abs(gestureState.dx) > 10;
    }

    /**
     * 表示申请成功,组件成为了事件处理响应者
     * @param event
     * @param gestureState
     * @private
     */
    _handlePanResponderGrant(event: Object, gestureState: Object): void {
        console.log('_handlePanResponderGrant');
    }

    /**
     * 处理滑动事件
     * @param event
     * @param gestureState
     * @private
     */
    _handlePanResponderMove(event: Object, gestureState: Object): void {
        if (this._previousLeft === null) {
            this._previousLeft = this.state.currentLeft._value
        }
        let nowLeft = this._previousLeft + gestureState.dx * 1;
        //右滑最大距离为0(边界值)
        nowLeft = Math.min(nowLeft, 0);
        this.state.currentLeft.setValue(
            nowLeft,
        );
    }

    /**
     * 结束事件的时候回调
     * @param event
     * @param gestureState
     * @private
     */
    _handlePanResponderEnd(event: Object, gestureState: Object): void {
        if(this._isOpen){
            if (Math.abs(this.state.currentLeft._value) >= 100) {
                this._animateToOpenPosition();
            } else {
                this._animateToClosedPosition();
            }
        }else{
            if (Math.abs(this.state.currentLeft._value) >= 100 / 3) {
                this._animateToOpenPosition();
            } else {
                this._animateToClosedPosition();
            }
        }
        this._previousLeft = null;
    }

    _shouldAnimateRemainder(gestureState: Object): boolean {
        /**
         * If user has swiped past a certain distance, animate the rest of the way
         * if they let go
         */
        return (
            Math.abs(gestureState.dx) > 100 / 3 ||
            gestureState.vx > 0.3
        );
    }

    _animateToOpenPosition(): void {
        this._isOpen = true;
        this._animateTo(-100);
    }

    _animateToClosedPosition(duration: number = 300): void {
        this._isOpen = false;
        this._animateTo(0, duration);
    }

    _animateTo(toValue, duration = 300, callback): void {
        Animated.spring(
            this.state.currentLeft,
            {
                toValue,
            }
        ).start((value) => {
        });
    }
}
const styles = StyleSheet.create({
    swipeContainer: {
        width: '100%',
    },
    swipeActions: {
        backgroundColor: 'grey',
        width: '100%',
        overflow: 'hidden',
        ...StyleSheet.absoluteFillObject,
        flexDirection: 'row',
        justifyContent: 'flex-end'
    },
});

我们运行项目:
这里写图片描述

好啦!!我们算是简单实现了我们的侧滑删除控件了,小伙伴自己动手敲一敲哦~

其实在RN的官方sdk中也是有侧滑删除控件的,只是fb没有在文档上暴露出来而已,下面我们就一起看看fb大牛是怎么封装控件的吧~

我们找到这么一个文件:
这里写图片描述

可以看到,fb直接把listview跟flatlist都封装了起来,我就不带着去研究了哈,我们重点看一下SwipeableRow.js

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule SwipeableRow
 * @flow
 */
'use strict';

const Animated = require('Animated');
const I18nManager = require('I18nManager');
const PanResponder = require('PanResponder');
const React = require('React');
const PropTypes = require('prop-types');
const StyleSheet = require('StyleSheet');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
 * found when Flow v0.54 was deployed. To see the error delete this comment and
 * run Flow. */
const TimerMixin = require('react-timer-mixin');
const View = require('View');

const createReactClass = require('create-react-class');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
 * found when Flow v0.54 was deployed. To see the error delete this comment and
 * run Flow. */
const emptyFunction = require('fbjs/lib/emptyFunction');

const IS_RTL = I18nManager.isRTL;

// NOTE: Eventually convert these consts to an input object of configurations

// Position of the left of the swipable item when closed
const CLOSED_LEFT_POSITION = 0;
// Minimum swipe distance before we recognize it as such
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 10;
// Minimum swipe speed before we fully animate the user's action (open/close)
const HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD = 0.3;
// Factor to divide by to get slow speed; i.e. 4 means 1/4 of full speed
const SLOW_SPEED_SWIPE_FACTOR = 4;
// Time, in milliseconds, of how long the animated swipe should be
const SWIPE_DURATION = 300;

/**
 * On SwipeableListView mount, the 1st item will bounce to show users it's
 * possible to swipe
 */
const ON_MOUNT_BOUNCE_DELAY = 700;
const ON_MOUNT_BOUNCE_DURATION = 400;

// Distance left of closed position to bounce back when right-swiping from closed
const RIGHT_SWIPE_BOUNCE_BACK_DISTANCE = 30;
const RIGHT_SWIPE_BOUNCE_BACK_DURATION = 300;
/**
 * Max distance of right swipe to allow (right swipes do functionally nothing).
 * Must be multiplied by SLOW_SPEED_SWIPE_FACTOR because gestureState.dx tracks
 * how far the finger swipes, and not the actual animation distance.
*/
const RIGHT_SWIPE_THRESHOLD = 30 * SLOW_SPEED_SWIPE_FACTOR;

/**
 * Creates a swipable row that allows taps on the main item and a custom View
 * on the item hidden behind the row. Typically this should be used in
 * conjunction with SwipeableListView for additional functionality, but can be
 * used in a normal ListView. See the renderRow for SwipeableListView to see how
 * to use this component separately.
 */
const SwipeableRow = createReactClass({
  displayName: 'SwipeableRow',
  _panResponder: {},
  _previousLeft: CLOSED_LEFT_POSITION,

  mixins: [TimerMixin],

  propTypes: {
    children: PropTypes.any,
    isOpen: PropTypes.bool,
    preventSwipeRight: PropTypes.bool,
    maxSwipeDistance: PropTypes.number.isRequired,
    onOpen: PropTypes.func.isRequired,
    onClose: PropTypes.func.isRequired,
    onSwipeEnd: PropTypes.func.isRequired,
    onSwipeStart: PropTypes.func.isRequired,
    // Should bounce the row on mount
    shouldBounceOnMount: PropTypes.bool,
    /**
     * A ReactElement that is unveiled when the user swipes
     */
    slideoutView: PropTypes.node.isRequired,
    /**
     * The minimum swipe distance required before fully animating the swipe. If
     * the user swipes less than this distance, the item will return to its
     * previous (open/close) position.
     */
    swipeThreshold: PropTypes.number.isRequired,
  },

  getInitialState(): Object {
    return {
      currentLeft: new Animated.Value(this._previousLeft),
      /**
       * In order to render component A beneath component B, A must be rendered
       * before B. However, this will cause "flickering", aka we see A briefly
       * then B. To counter this, _isSwipeableViewRendered flag is used to set
       * component A to be transparent until component B is loaded.
       */
      isSwipeableViewRendered: false,
      rowHeight: (null: ?number),
    };
  },

  getDefaultProps(): Object {
    return {
      isOpen: false,
      preventSwipeRight: false,
      maxSwipeDistance: 0,
      onOpen: emptyFunction,
      onClose: emptyFunction,
      onSwipeEnd: emptyFunction,
      onSwipeStart: emptyFunction,
      swipeThreshold: 30,
    };
  },

  UNSAFE_componentWillMount(): void {
    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminationRequest: this._onPanResponderTerminationRequest,
      onPanResponderTerminate: this._handlePanResponderEnd,
      onShouldBlockNativeResponder: (event, gestureState) => false,
    });
  },

  componentDidMount(): void {
    if (this.props.shouldBounceOnMount) {
      /**
       * Do the on mount bounce after a delay because if we animate when other
       * components are loading, the animation will be laggy
       */
      this.setTimeout(() => {
        this._animateBounceBack(ON_MOUNT_BOUNCE_DURATION);
      }, ON_MOUNT_BOUNCE_DELAY);
    }
  },

  UNSAFE_componentWillReceiveProps(nextProps: Object): void {
    /**
     * We do not need an "animateOpen(noCallback)" because this animation is
     * handled internally by this component.
     */
    if (this.props.isOpen && !nextProps.isOpen) {
      this._animateToClosedPosition();
    }
  },

  render(): React.Element<any> {
    // The view hidden behind the main view
    let slideOutView;
    if (this.state.isSwipeableViewRendered && this.state.rowHeight) {
      slideOutView = (
        <View style={[
          styles.slideOutContainer,
          {height: this.state.rowHeight},
          ]}>
          {this.props.slideoutView}
        </View>
      );
    }

    // The swipeable item
    const swipeableView = (
      <Animated.View
        onLayout={this._onSwipeableViewLayout}
        style={{transform: [{translateX: this.state.currentLeft}]}}>
        {this.props.children}
      </Animated.View>
    );

    return (
      <View
        {...this._panResponder.panHandlers}>
        {slideOutView}
        {swipeableView}
      </View>
    );
  },

  close(): void {
    this.props.onClose();
    this._animateToClosedPosition();
  },

  _onSwipeableViewLayout(event: Object): void {
    this.setState({
      isSwipeableViewRendered: true,
      rowHeight: event.nativeEvent.layout.height,
    });
  },

  _handleMoveShouldSetPanResponderCapture(
    event: Object,
    gestureState: Object,
  ): boolean {
    // Decides whether a swipe is responded to by this component or its child
    return gestureState.dy < 10 && this._isValidSwipe(gestureState);
  },

  _handlePanResponderGrant(event: Object, gestureState: Object): void {

  },

  _handlePanResponderMove(event: Object, gestureState: Object): void {
    if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
      return;
    }

    this.props.onSwipeStart();

    if (this._isSwipingRightFromClosed(gestureState)) {
      this._swipeSlowSpeed(gestureState);
    } else {
      this._swipeFullSpeed(gestureState);
    }
  },

  _isSwipingRightFromClosed(gestureState: Object): boolean {
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0;
  },

  _swipeFullSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
  },

  _swipeSlowSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(
      this._previousLeft + gestureState.dx / SLOW_SPEED_SWIPE_FACTOR,
    );
  },

  _isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
    /**
     * We want to allow a BIT of right swipe, to allow users to know that
     * swiping is available, but swiping right does not do anything
     * functionally.
     */
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return (
      this._isSwipingRightFromClosed(gestureState) &&
      gestureStateDx > RIGHT_SWIPE_THRESHOLD
    );
  },

  _onPanResponderTerminationRequest(
    event: Object,
    gestureState: Object,
  ): boolean {
    return false;
  },

  _animateTo(
    toValue: number,
    duration: number = SWIPE_DURATION,
    callback: Function = emptyFunction,
  ): void {
    Animated.timing(
      this.state.currentLeft,
      {
        duration,
        toValue,
        useNativeDriver: true,
      },
    ).start(() => {
      this._previousLeft = toValue;
      callback();
    });
  },

  _animateToOpenPosition(): void {
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance);
  },

  _animateToOpenPositionWith(
    speed: number,
    distMoved: number,
  ): void {
    /**
     * Ensure the speed is at least the set speed threshold to prevent a slow
     * swiping animation
     */
    speed = (
      speed > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD ?
      speed :
      HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
    /**
     * Calculate the duration the row should take to swipe the remaining distance
     * at the same speed the user swiped (or the speed threshold)
     */
    const duration = Math.abs((this.props.maxSwipeDistance - Math.abs(distMoved)) / speed);
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance, duration);
  },

  _animateToClosedPosition(duration: number = SWIPE_DURATION): void {
    this._animateTo(CLOSED_LEFT_POSITION, duration);
  },

  _animateToClosedPositionDuringBounce(): void {
    this._animateToClosedPosition(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
  },

  _animateBounceBack(duration: number): void {
    /**
     * When swiping right, we want to bounce back past closed position on release
     * so users know they should swipe right to get content.
     */
    const swipeBounceBackDistance = IS_RTL ?
      -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
      RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
    this._animateTo(
      -swipeBounceBackDistance,
      duration,
      this._animateToClosedPositionDuringBounce,
    );
  },

  // Ignore swipes due to user's finger moving slightly when tapping
  _isValidSwipe(gestureState: Object): boolean {
    if (this.props.preventSwipeRight && this._previousLeft === CLOSED_LEFT_POSITION && gestureState.dx > 0) {
      return false;
    }

    return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
  },

  _shouldAnimateRemainder(gestureState: Object): boolean {
    /**
     * If user has swiped past a certain distance, animate the rest of the way
     * if they let go
     */
    return (
      Math.abs(gestureState.dx) > this.props.swipeThreshold ||
      gestureState.vx > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
  },

  _handlePanResponderEnd(event: Object, gestureState: Object): void {
    const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx;
    if (this._isSwipingRightFromClosed(gestureState)) {
      this.props.onOpen();
      this._animateBounceBack(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
    } else if (this._shouldAnimateRemainder(gestureState)) {
      if (horizontalDistance < 0) {
        // Swiped left
        this.props.onOpen();
        this._animateToOpenPositionWith(gestureState.vx, horizontalDistance);
      } else {
        // Swiped right
        this.props.onClose();
        this._animateToClosedPosition();
      }
    } else {
      if (this._previousLeft === CLOSED_LEFT_POSITION) {
        this._animateToClosedPosition();
      } else {
        this._animateToOpenPosition();
      }
    }

    this.props.onSwipeEnd();
  },
});

const styles = StyleSheet.create({
  slideOutContainer: {
    bottom: 0,
    left: 0,
    position: 'absolute',
    right: 0,
    top: 0,
  },
});

module.exports = SwipeableRow;

小伙伴看了是不是觉得很熟悉呢?哈哈~对的!思路大概跟我们的差不多,只不过把一些渲染跟变量抽离出来了,想必小伙伴都能很轻松的看懂者代码了,因为项目中会出现一个侧滑的删除事件跟flatlist跟listview这类控件的手势冲突,那我们就看看fb那帮老头是咋解决冲突的,

我们可以看到在手势开始也就是_handlePanResponderMove方法中:

  _handlePanResponderMove(event: Object, gestureState: Object): void {
    if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
      return;
    }

    this.props.onSwipeStart();

    if (this._isSwipingRightFromClosed(gestureState)) {
      this._swipeSlowSpeed(gestureState);
    } else {
      this._swipeFullSpeed(gestureState);
    }
  },

用了一个回调:

 this.props.onSwipeStart();

那我就去SwipeableFlatList.js看看回调后看了啥:

 return (
      <SwipeableRow
        slideoutView={slideoutView}
        isOpen={key === this.state.openRowKey}
        maxSwipeDistance={this._getMaxSwipeDistance(info)}
        onOpen={() => this._onOpen(key)}
        onClose={() => this._onClose(key)}
        shouldBounceOnMount={shouldBounceOnMount}
        onSwipeEnd={this._setListViewScrollable}
        onSwipeStart={this._setListViewNotScrollable}>
        {this.props.renderItem(info)}
      </SwipeableRow>
    );

可以看到调用了_setListViewNotScrollable方法:

 _setListViewNotScrollable = () => {
    this._setListViewScrollableTo(false);
  };

继续走最后调用了:

  _setListViewScrollableTo(value: boolean) {
    if (this._flatListRef) {
      this._flatListRef.setNativeProps({
        scrollEnabled: value,
      });
    }
  }

好吧,看到这里大家是不是有点感触了,这里的_flatListRef引用就是我们的flatlist,然后通过setNativeProps方法把flatlist的scrollEnabled设为了false,(也就是当滑动的时候通知flatlist你不要滑动了)然后当手指抬起的时候告诉flatlist,你可以滑动了,也就是下面方法。

 onSwipeEnd={this._setListViewScrollable}
  _setListViewScrollable = () => {
    this._setListViewScrollableTo(true);
  };

好啦~说到这里我们不禁对rn的事件传递机制提出了疑问,明明子控件都已经消耗掉了滑动事件,为啥父控件还会收到事件,盗用下网上帖子的一张图:
这里写图片描述

我们的C组件onMoveShouldSetPanResponderCapture都直接返回true了,为啥还会走到A组件????我也有点疑惑哈~还没去研究native源码的,小伙伴知道的可以跟我说说哈~拜谢!!!

好啦! 看完事件冲突,我们顺便研究下setNativeProps方法,

大家多多少少用过或者了解过setNativeProps方法,我们简单的带着大家撸一撸源码,我们打开View的源码,
这里写图片描述

大家可以看到,view里面根本就没有这个方法,于是我们看看mixins里面有没有?,果然是看到了~~

mixins: [NativeMethodsMixin],

我们点开NativeMethodsMixin文件:

'use strict';

const {
  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactNative');

import type {NativeMethodsMixinType} from 'ReactNativeTypes';

const {NativeMethodsMixin} = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;

module.exports = ((NativeMethodsMixin: any): $Exact<NativeMethodsMixinType>);

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED这尼玛是什么鬼?不懂!继续走:

'use strict';

import type {ReactNativeType} from 'ReactNativeTypes';

let ReactNative;

if (__DEV__) {
  ReactNative = require('ReactNativeRenderer-dev');
} else {
  ReactNative = require('ReactNativeRenderer-prod');
}

module.exports = (ReactNative: ReactNativeType);

我们最终在ReactNativeRenderer-dev.js中找到了NativeMethodsMixin对象,然后看到了setNativeProps方法:

 setNativeProps: function(nativeProps) {
    // Class components don't have viewConfig -> validateAttributes.
    // Nor does it make sense to set native props on a non-native component.
    // Instead, find the nearest host component and set props on it.
    // Use findNodeHandle() rather than findNumericNodeHandle() because
    // We want the instance/wrapper (not the native tag).
    var maybeInstance = void 0;

    // Fiber errors if findNodeHandle is called for an umounted component.
    // Tests using ReactTestRenderer will trigger this case indirectly.
    // Mimicking stack behavior, we should silently ignore this case.
    // TODO Fix ReactTestRenderer so we can remove this try/catch.
    try {
      maybeInstance = findNodeHandle(this);
    } catch (error) {}

    // If there is no host component beneath this we should fail silently.
    // This is not an error; it could mean a class component rendered null.
    if (maybeInstance == null) {
      return;
    }

    var viewConfig = maybeInstance.viewConfig;

    {
      warnForStyleProps(nativeProps, viewConfig.validAttributes);
    }

    var updatePayload = create(nativeProps, viewConfig.validAttributes);

    // Avoid the overhead of bridge calls if there's no update.
    // This is an expensive no-op for Android, and causes an unnecessary
    // view invalidation for certain components (eg RCTTextInput) on iOS.
    if (updatePayload != null) {
      UIManager.updateView(
        maybeInstance._nativeTag,
        viewConfig.uiViewClassName,
        updatePayload
      );
    }
  },

其实最主要就是执行了:

UIManager.updateView(
        maybeInstance._nativeTag,
        viewConfig.uiViewClassName,
        updatePayload
      );

UIManager是native的模块,所以我们看看native干了啥:
这里写图片描述

  @ReactMethod
    public void updateView(int tag, String className, ReadableMap props) {
        this.mUIImplementation.updateView(tag, className, props);
    }

好吧~ android里面逻辑有点复杂了,我简单说一下思路 native根据传tag findviewbyid找到特定的view—>然后根据传过来的uiViewClassName找到manager—>把updatePayload传给manager–>最后直接view.setxxx设置属性

我很天真的认为所有的自定义组件也可以直接调用:

 UIManager.updateView(
        maybeInstance._nativeTag,
        viewConfig.uiViewClassName,
        updatePayload
      );

如果可以直接调用,我就可以直接修改我直接view的某些属性而不需要render了,但是事实是native根据传tag findviewbyid找到特定的view—>然后根据传过来的uiViewClassName找到manager—>把updatePayload传给manager–>最后直接view.setxxx设置属性;

然后根据传过来的uiViewClassName找到manage走到这一步的时候拿到的manager是viewmanager,尴尬!!!! 好吧~ 有知道的童鞋告知一下,拜谢!!!

猜你喜欢

转载自blog.csdn.net/vv_bug/article/details/79551403