I draw a button at bottom: 0
in my React Native app (RN 0.42). This works pretty well on iOS and the most Android devices. But on Android devices with no physical hardware buttons, the button bar is drawn on the screen right above my button.
So: Is there a way to detect whether the hardware buttons are physical or software driven to adjust my layout in this case? Or is this just a bug in React Native since this only happens in modal dialogs?
Nexus (the green button has the style: bottom: 45
and the hardware buttons are rendered on the screen):
Galaxy S5: (this device has real hardware buttons, so the bottom: 45
is too much):
It appears to be easy using Java to determine whether an Android device has a physical button, so to access that in React Native I would make a Native Module wrapper to return the result of ViewConfiguration.get(context).hasPermanentMenuKey()
.
You can open the /android
directory of your React Native project in Android Studio, then within the /app/src/main/java/<com.companyname.appname>/
folder, at the same level where you see MainActivity
and MainApplication
files, create a new Android resource directory called detecthardware
(or any other appropriate name). Within that create DetectHardwareModule.java and include this:
package com.companyname.appname.detecthardware;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
import java.util.List;
public class DetectHardwareModule extends ReactContextBaseJavaModule {
public DetectHardwareModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "DetectHardware";
}
@ReactMethod
public void hasHardwareButtons(final Callback callback) {
callback.invoke(ViewConfiguration.get(getReactApplicationContext()).hasPermanentMenuKey());
}
}
Then create DetectHardwarePackage.java and include this:
package com.companyname.appname.detecthardware;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DetectHardwarePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DetectHardwareModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Inside your MainApplication.java you'll need to include a getPackages
method so the JavaScript code can access it:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new DetectHardwarePackage()
);
}
Now within your React Native component, make sure you import NativeModules
from react-native
, as well as Platform
so that you only run the function when on Android. In componentDidMount
:
componentDidMount() {
if (Platform.OS === 'android') {
NativeModules.DetectHardware.hasHardwareButtons(function(result) {
this.setState({hasHardwareButtons: result})
}.bind(this));
}
}
And finally within your render
function, calculate the bottom
amount to be 0 if the platform is android and hasHardwareButtons is true, otherwise 45:
var bottom = (Platform.OS === 'android' && this.state.hasHardwareButtons) ? 0 : 45
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With