1. 程式人生 > >ReactNative 呼叫Android 原生(二)——原生元件

ReactNative 呼叫Android 原生(二)——原生元件

先說下我遇到的坑: 原本學完React Native呼叫原生模組我覺得挺順利的,也挺容易理解的,覺得呼叫Android原生元件也是不成問題,應該挺快的,結果React Native 調原生元件一直不顯示,在網上找了好多文章對比看,覺得自己程式碼、步驟都沒有問題,呼叫控制元件不顯示的原因文章末尾跟程式碼的註釋裡面我都將說明,免得有跟我一樣入坑的!

步驟

1、繼承SimpleViewManager 2、實現ReactPackage 3、向MainApplication新增ReactPackage 4、RN中匯出View 5、使用匯出的View

使用

如果你看過ReactNative 呼叫Android 原生(一)——原生模組(一)、(二)就很熟悉這個流程了,下面這個Demo是個倒計時的效果 在這裡插入圖片描述

1、繼承SimpleViewManager, (1)getName()返回的名字在RN中將被用到 (2) @ReactProp(name = “start”) 中@ReactProp註解用於定義方法,name = "start"中的start就是RN中匯出的View可以使用的屬性名

public class TestUI extends SimpleViewManager<TimerView> {
    @Override
    public String getName() {
        return "TimerView";
    }

    @Override
    protected TimerView createViewInstance(ThemedReactContext reactContext) {

        return new TimerView(reactContext);
    }

    @ReactProp(name = "start")
    public void startTimer(TimerView timerView, Integer totalCount) {
          timerView.startTimer(totalCount);
    }
}

補充:

public class TimerView extends LinearLayout {
    private Timer mTimer;
    private TimerTask mTimerTask;
    private TextView mTimerTv;
    private Context mContext;
    private int mCount;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mTimerTv.setText(mCount + "");
            int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY);
            int h = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY);
            measure(
                    w,
                    h);
            layout(getLeft(), getTop(), getRight(), getBottom());
        }
    };

    public TimerView(Context context) {
        this(context,null);
    }

    public TimerView(Context context, @Nullable AttributeSet attrs) {
        super(context);
        init(context);
    }


    private void init(Context context) {
        mContext = context;
        //佈局的話隨便載入
        View view = LayoutInflater.from(context).inflate(R.layout.timer_view, this);
        view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
        mTimerTv = view.findViewById(R.id.timer_tv);
        mTimerTv.setText(10 + "");
    }

//呼叫該方法,開始倒計時
    public void startTimer(int totalcount) {
        mCount = totalcount;
        mTimerTv.setText(totalcount + "");
        mTimer = new Timer();
        mTimerTask = new TimerTask() {
            @Override
            public void run() {
                if (mCount >= 0) {
                    --mCount;
                    mHandler.sendEmptyMessage(0);
                } else {
                    if (mTimer != null) {
                        mTimer.cancel();
                        mTimer = null;
                    }
                }
            }
        };
        mTimer.schedule(mTimerTask, 0, 1000);
    }
    }

2、實現ReactPackage

public class TestPackages implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> nativeModules = new ArrayList<>();
        nativeModules.add(new TestModules(reactContext));
        return nativeModules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(
                new TestUI()
        );
    }
}

3、向MainApplication新增ReactPackage

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    new TestPackages()
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}

4、RN中匯出View 這裡就是坑了,必須寫style並設定寬高,否則不顯示 屬性名:start對應上面的

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
  requireNativeComponent,
  View
} from 'react-native';

//Android 原生View 對映成AndroidTimerView,匯出為NativeTimerView
const AndroidTimerView = requireNativeComponent('TimerView', NativeTimerView);
export default class NativeTimerView extends Component<Props> {
  static propTypes = {
    stimer: PropTypes.number,
  };

  render() {
    return <AndroidTimerView
      style={{width: 100, height: 50, borderRadius: 20}}
      start={this.props.stimer}
    />
  }
}

5、使用匯出的View 如果你在第4步中沒有設定style 在這裡對NativeTimerView設定style的寬高,執行後仍然會什麼都不顯示,因此必須在第4步中設定寬高

export default class App extends Component<Props> {
  constructor() {
    super();
    this.state = {
      name: 'To get started, edit App.js',
      sendEvent: 'Send Event'
    }
  }

  toast = () => {
    let obj = {
      id: 1,
      name: "xiaohong"
    };
    NativeModules.TestModules.Toasts(obj);
  };
  toastFinishCallback = () => {
    NativeModules.TestModules.ToastFinished("呼叫了原生並帶有回撥", (result) => {
      this.setState({
        name: result.name
      });
    });
  };

  sendEvent = () => {
    NativeModules.TestModules.sendEvent();
  };

  componentWillMount() {
    DeviceEventEmitter.addListener('sendEvent', (e: Event) => {
      this.setState({
        sendEvent: e.name
      });
    })
  };

  startActivity = () => {
    NativeModules.TestModules.startActivity();
  };

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}
              onPress={this.toast}
        >Welcome to React Native!</Text>
        <Text style={styles.instructions}
              onPress={this.toastFinishCallback}
        >{this.state.name}</Text>
        <Text style={styles.instructions}
              onPress={this.sendEvent}
        >{this.state.sendEvent}</Text>

        <Text
          style={styles.instructions}
          onPress={this.startActivity}
        >點選開啟activity</Text>

        <NativeTimerView
          style={{width: 100, height: 50, borderRadius: 20}}
          stimer={60}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

總結

我的之前顯示不出來就是因為我在第5步設定的寬高,而沒有在第4步設定寬高,結果就一直顯示不出來,希望不要有人再犯我這樣的錯誤了。