我目前正在尝试为 React Native 创建本地库。我的 React Native 应用程序在旧设备上的指南针动画确实很吃力,因此我试图将这部分移至本机,以便在 Android 上获得更好的性能。
这是我第一次尝试构建原生组件,我还没有找到一个很好的信息源来解释我正在尝试做的事情。
原生 android 组件的 React Native 文档将我指向一个带有 SimpleViewManager 的 java 类,这个类可以设置视图及其属性。我正在努力解决的问题是在这个视图中实现一个 compassActivity 。最终目标是在 React Native 中导入一个 API,该 API 可以同时处理设备的航向信息和指南针动画。我的第一种方法是在 createViewInstance 中返回 CompassActivity,但这会导致很多错误。
这是我的视图管理器:
package com.nativeview;
import android.graphics.Color;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
public class VideoViewManager extends SimpleViewManager<View> {
public static final String REACT_CLASS = "CompassView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
@NonNull
public View createViewInstance(ThemedReactContext reactContext) {
return new View(reactContext);
}
@ReactProp(name = "color")
public void setColor(View view, String color) {
view.setBackgroundColor(Color.parseColor(color));
}
}
CompassActivity 看起来像这样:
package com.nativeview;
import android.net.Uri;
import android.util.Log;
import android.widget.VideoView;
import com.facebook.react.ReactActivity;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;
public class CompassActivity extends AppCompatActivity {
private static final String TAG = "CompassActivity";
private Compass compass;
private ImageView arrowView;
private TextView sotwLabel; // SOTW is for "side of the world"
private float currentAzimuth;
private SOTWFormatter sotwFormatter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_compass);
sotwFormatter = new SOTWFormatter(this);
arrowView = findViewById(R.id.main_image_hands);
sotwLabel = findViewById(R.id.sotw_label);
setupCompass();
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "start compass");
compass.start();
}
@Override
protected void onPause() {
super.onPause();
compass.stop();
}
@Override
protected void onResume() {
super.onResume();
compass.start();
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "stop compass");
compass.stop();
}
private void setupCompass() {
compass = new Compass(this);
Compass.CompassListener cl = getCompassListener();
compass.setListener(cl);
}
private void adjustArrow(float azimuth) {
Log.d(TAG, "will set rotation from " + currentAzimuth + " to "
+ azimuth);
Animation an = new RotateAnimation(-currentAzimuth, -azimuth,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
currentAzimuth = azimuth;
an.setDuration(500);
an.setRepeatCount(0);
an.setFillAfter(true);
arrowView.startAnimation(an);
}
private void adjustSotwLabel(float azimuth) {
sotwLabel.setText(sotwFormatter.format(azimuth));
}
private Compass.CompassListener getCompassListener() {
return new Compass.CompassListener() {
@Override
public void onNewAzimuth(final float azimuth) {
// UI updates only in UI thread
// https://stackoverflow.com/q/11140285/444966
runOnUiThread(new Runnable() {
@Override
public void run() {
adjustArrow(azimuth);
adjustSotwLabel(azimuth);
}
});
}
};
}
}
最后一部分是 Compass 类,如下所示:
package com.nativeview;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class Compass implements SensorEventListener {
private static final String TAG = "Compass";
public interface CompassListener {
void onNewAzimuth(float azimuth);
}
private CompassListener listener;
private SensorManager sensorManager;
private Sensor gsensor;
private Sensor msensor;
private float[] mGravity = new float[3];
private float[] mGeomagnetic = new float[3];
private float[] R = new float[9];
private float[] I = new float[9];
private float azimuth;
private float azimuthFix;
public Compass(Context context) {
sensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
public void start() {
sensorManager.registerListener(this, gsensor,
SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(this, msensor,
SensorManager.SENSOR_DELAY_GAME);
}
public void stop() {
sensorManager.unregisterListener(this);
}
public void setAzimuthFix(float fix) {
azimuthFix = fix;
}
public void resetAzimuthFix() {
setAzimuthFix(0);
}
public void setListener(CompassListener l) {
listener = l;
}
@Override
public void onSensorChanged(SensorEvent event) {
final float alpha = 0.97f;
synchronized (this) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity[0] = alpha * mGravity[0] + (1 - alpha)
* event.values[0];
mGravity[1] = alpha * mGravity[1] + (1 - alpha)
* event.values[1];
mGravity[2] = alpha * mGravity[2] + (1 - alpha)
* event.values[2];
// mGravity = event.values;
// Log.e(TAG, Float.toString(mGravity[0]));
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
// mGeomagnetic = event.values;
mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha)
* event.values[0];
mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha)
* event.values[1];
mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha)
* event.values[2];
// Log.e(TAG, Float.toString(event.values[0]));
}
boolean success = SensorManager.getRotationMatrix(R, I, mGravity,
mGeomagnetic);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
// Log.d(TAG, "azimuth (rad): " + azimuth);
azimuth = (float) Math.toDegrees(orientation[0]); // orientation
azimuth = (azimuth + azimuthFix + 360) % 360;
// Log.d(TAG, "azimuth (deg): " + azimuth);
if (listener != null) {
listener.onNewAzimuth(azimuth);
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
我真的不知道这是否可能以及哪种策略效果最好,如果有人能指出我正确的方向,我将不胜感激。