In one of my react native project I needed to add a custom surfaceview where we will render some content. So before I add anything to the main project I created a test react native project.
Here is my custom surfaeview. It's pretty straight forward. Just shows a red ball moving.
BallSurfaceView.java
package com.myreactnative;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class BallSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable{
private static final String TAG = "BallSurfaceView";
private SurfaceHolder holder;
private boolean runFlag;
private Paint paint = new Paint();
//Rewrite the constructor
public BallSurfaceView(Context context) {
this(context, null);
}
public BallSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BallSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Get the Surface handle object, get the canvas through the object
holder = this.getHolder();
// Add callback interface
holder.addCallback(this);
// setFocusable(true);
}
//Method to implement the SurfaceHolder.Callback interface
@Override//call when switching between horizontal and vertical
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
//Called when the SurfaceView is created
@Override
public void surfaceCreated(SurfaceHolder arg0) {
//Start the thread when creating
runFlag = true;
new Thread(this).start();
}
//Called when the SurfaceView ends
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
//End the thread when exiting
runFlag = false;
}
//Rewrite the run method
@Override
public void run() {
int x = 0;
int y = 0;
int dx = 2;
int dy = 3;
int width = 30;
int height = 30;
while(runFlag){
Canvas canvas = null;
try {
//Lock the brush object
canvas = holder.lockCanvas();
//Set the brush
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
//Fill the background color
Rect rect = new Rect(0, 0, this.getWidth(), this.getHeight());
canvas.drawRect(rect, paint);
//draw a small ball, set the color of the ball to red
paint.setColor(Color.RED);
RectF rf = new RectF(x, y, x+width, y+height);
canvas.drawOval(rf, paint);
//Small ball moves
x += dx;
y += dy;
//Boundary judgment
if(x<=0 || x>=this.getWidth()){
dx = -dx;
}
if(y<=0 || y>=this.getHeight()){
dy = -dy;
}
Log.e(TAG,"run x: "+x+" y:"+y);
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//When the canvas is used up, release it.
if(canvas != null){
holder.unlockCanvasAndPost(canvas);
}
}
}
}
public void setChannelName() {
Log.e(TAG,"----------------------------------");
Log.e(TAG,"setChannelName----------------------------------");
Log.e(TAG,"----------------------------------");
}
}
Now to use this in my react native part here are the two files that I used.
vv.js
'use strict';
import React,{ Component,PropTypes }from 'react';
import {
requireNativeComponent,
View,
UIManager,
findNodeHandle,
}from 'react-native';
var RCT_VIDEO_REF = 'VideoView';
class VideoView extends Component {
constructor(props) {
super(props);
}
pause(){
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.refs[RCT_VIDEO_REF]),
UIManager.RV.Commands.pause,
null
);
}
start(){
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.refs[RCT_VIDEO_REF]),
UIManager.RV.Commands.start,
null
);
}
render(){
return <RCTVideoView
{...this.props}
ref = {RCT_VIDEO_REF}
/>;
};
}
var RCTVideoView = requireNativeComponent('RV',VideoView,{
nativeOnly: {onChange: true}
});
module.exports = VideoView;
index.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,
processColor,
TouchableOpacity
} from 'react-native';
import VideoView from './vv';
export default class untitled extends Component {
onPressPause(){
this.video.pause();
}
onPressStart(){
this.video.start();
}
render() {
return (
<View style={styles.container}>
<VideoView
style={{height:450,width:580}}
ref={(video)=>{this.video = video}}
/>
<View style={{height:50,flexDirection:'row',justifyContent:'flex-start'}}>
<TouchableOpacity style={{marginLeft:10}} onPress={this.onPressPause.bind(this)}>
<Text> Pause </ Text>
</TouchableOpacity>
<TouchableOpacity style={{marginLeft:10}} onPress={this.onPressStart.bind(this)}>
<Text> Start </ Text>
</TouchableOpacity>
</View>
</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,
},
});
AppRegistry.registerComponent('main',() => untitled);
Now, when I run this project everything seems fine. I can see the view being rendered without any problem.
Now when I add this code to my main react native project everything works but view does not render.
I know react uses virtual dom to compare the differences to render a view. Since I am not setting any state or props and customview update is happening on native side why is that react native is updating view in one project while it's not updating the view in other project.
What concept am I missing?
I am a native android developer. So, if you can point me in the right direction that would be great.