Merge pull request #184 from ndbroadbent/consistent-api
Consistent API for Android and iOS
This commit is contained in:
commit
c053fa3697
243
README.md
243
README.md
|
@ -1,20 +1,23 @@
|
|||
[![npm version](https://badge.fury.io/js/react-native-blur.svg)](https://badge.fury.io/js/react-native-blur)
|
||||
|
||||
### React Native Blur
|
||||
Component implementation for UIVisualEffectView's blur and vibrancy effect.<br>
|
||||
Check the [roadmap here](https://github.com/Kureev/react-native-blur/issues/1)
|
||||
|
||||
<img src='https://cloud.githubusercontent.com/assets/5795227/20123354/d877dba4-a61e-11e6-8e5a-c85f76419e20.gif' />
|
||||
A component for UIVisualEffectView's blur and vibrancy effect on iOS, and [500px-android-blur](https://github.com/500px/500px-android-blur) on Android.<br>
|
||||
|
||||
<img src='https://cloud.githubusercontent.com/assets/139536/25066337/3c9d44c0-224d-11e7-8ca6-028478bf4a7d.gif' />
|
||||
|
||||
|
||||
### Content
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage example](#usage-example)
|
||||
- [Blur view](#blur-view)
|
||||
- [Vibrancy view](#vibrancy-view)
|
||||
- [Component properties](#component-properties)
|
||||
- [BlurView](#blurview)
|
||||
- [VibrancyView](#vibrancyview)
|
||||
- [Example React Native app](#example-react-native-app)
|
||||
- [Questions?](#questions)
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
1. Install package via npm:
|
||||
|
||||
```
|
||||
|
@ -25,61 +28,146 @@ Check the [roadmap here](https://github.com/Kureev/react-native-blur/issues/1)
|
|||
```
|
||||
react-native link react-native-blur
|
||||
```
|
||||
3. (Android only) Add the following to your `android/app/build.gradle`
|
||||
|
||||
`android/build.gradle`
|
||||
3. (Android only) Add the following to `android/app/build.gradle`
|
||||
|
||||
```
|
||||
buildscript {
|
||||
dependencies {
|
||||
// Update "Android Plugin for Gradle" version
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
// Add the following in a new line, underneath "maven { url '$rootDir/../node_modules/react-native/android' }"
|
||||
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`android/app/build.gradle`
|
||||
```
|
||||
dependencies {
|
||||
compile 'com.fivehundredpx:blurringview:1.0.0'
|
||||
}
|
||||
|
||||
|
||||
android {
|
||||
// ...
|
||||
defaultConfig {
|
||||
// Add these to the existing config
|
||||
// Add these lines below the existing config
|
||||
renderscriptTargetApi 23
|
||||
renderscriptSupportModeEnabled true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`android/gradle/wrapper/gradle-wrapper.properties`
|
||||
```
|
||||
// Update Gradle version
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
```
|
||||
|
||||
4. Inside your code include JS part by adding
|
||||
4. Include the library in your code:
|
||||
|
||||
```javascript
|
||||
import { BlurView, VibrancyView } from 'react-native-blur';
|
||||
|
||||
// OR
|
||||
|
||||
const { BlurView, VibrancyView } = require('react-native-blur');
|
||||
```
|
||||
|
||||
5. Compile and have fun!
|
||||
|
||||
### Usage example
|
||||
|
||||
You can run the built-in examples by running these steps:
|
||||
### BlurView
|
||||
|
||||
**Properties:**
|
||||
|
||||
- `blurType` (String)
|
||||
- `xlight` - extra light blur type
|
||||
- `light` - light blur type
|
||||
- `dark` - dark blur type
|
||||
- `blurAmount` (Default: 10, Number)
|
||||
- `0-100` - Adjusts blur intensity
|
||||
|
||||
> Note: The maximum `blurAmount` on Android is 32, so higher values will be clamped to 32.
|
||||
|
||||
> Complete usage example that works on iOS and Android:
|
||||
|
||||
```javascript
|
||||
import React, { Component } from 'react';
|
||||
import { View, Image, Text, findNodeHandle, StyleSheet } from 'react-native';
|
||||
import { BlurView } from 'react-native-blur';
|
||||
|
||||
export default class Menu extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { viewRef: null };
|
||||
}
|
||||
|
||||
imageLoaded() {
|
||||
this.setState({ viewRef: findNodeHandle(this.backgroundImage) });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Image
|
||||
ref={(img) => { this.backgroundImage = img; }}
|
||||
source={{uri}}
|
||||
style={styles.absolute}
|
||||
onLoadEnd={this.imageLoaded.bind(this)}
|
||||
/>
|
||||
<BlurView
|
||||
style={styles.absolute}
|
||||
viewRef={this.state.viewRef}
|
||||
blurType="light"
|
||||
blurAmount={10}
|
||||
/>
|
||||
<Text>Hi, I am some unblurred text</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
absolute: {
|
||||
position: "absolute",
|
||||
top: 0, left: 0, bottom: 0, right: 0,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
In this example, the `Image` component will be blurred, because the `BlurView` in positioned on top. But the `Text` will stay unblurred.
|
||||
|
||||
Note that `viewRef` is only required if you need to support Android. See the [Android section](#android) for more details.
|
||||
|
||||
### VibrancyView
|
||||
|
||||
**Uses the same properties as `BlurView` (`blurType` and `blurAmount`).**
|
||||
|
||||
> The vibrancy effect lets the content underneath a blurred view show through more vibrantly
|
||||
|
||||
> (Note: `VibrancyView` is only supported on iOS. Also note that the `VibrancyView` must contain nested views.)
|
||||
|
||||
```javascript
|
||||
import { VibrancyView } from 'react-native-blur';
|
||||
|
||||
export default class Menu extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Image source={{uri}} style={styles.absolute}>
|
||||
<VibrancyView blurType="light" style={styles.flex}>
|
||||
<Text>Hi, I am some vibrant text.</Text>
|
||||
</VibrancyView>
|
||||
</Image>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
Android uses the [500px-android-blur](https://github.com/500px/500px-android-blur) library, which works by blurring a referenced view. This means that you must wait until the view you want to blur is rendered. You then use `findNodeHandle` to get a reference to that view, and pass that reference to the `BlurView` as the `viewRef` prop. Take a look at [the BlurView example](#blurview) to see how it works.
|
||||
|
||||
The Android library introduces some limitations:
|
||||
|
||||
* `BlurView` cannot be a child of the view that is being blurred (this would cause an infinite loop)
|
||||
* `BlurView` cannot contain any child components.
|
||||
|
||||
If you only need to support iOS, then you can safely ignore these limitations.
|
||||
|
||||
In addition to `blurType` and `blurAmount`, Android has some extra props that can be used to override the default behavior (or configure Android-specific behavior):
|
||||
|
||||
- `blurRadius` (Number - between 0 and 25) - Manually adjust the blur radius. (Default: matches iOS blurAmount)
|
||||
- `downsampleFactor` (Number - between 0 and 25) - Scales down the image before blurring (Default: matches iOS blurAmount)
|
||||
- `overlayColor` (Color) - Set a custom overlay color (Default color based on iOS blurType)
|
||||
|
||||
|
||||
### Example React Native App
|
||||
|
||||
This project includes an example React Native app, which was used to make the GIF in this README.
|
||||
You can run the apps by following these steps:
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
|
@ -114,74 +202,9 @@ react-native run-ios
|
|||
react-native run-android
|
||||
```
|
||||
|
||||
#### Blur View
|
||||
|
||||
To use `blur` view, you need to require `BlurView` to your module and insert `<BlurView>` tag inside render function as it's done below:
|
||||
### Troubleshooting
|
||||
|
||||
```javascript
|
||||
const { BlurView } = require('react-native-blur');
|
||||
|
||||
const Menu = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Image source={{uri}} style={styles.menu} />
|
||||
<BlurView blurType="light" blurAmount={10} style={styles.blur} />
|
||||
<Text style={styles.text}>Hi, I am a tiny menu item</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In this example, `Image` component will be blurred, a `BlurView` content will stay untouched.
|
||||
|
||||
Note that if you need to support Android, the `BlurView` cannot be a child of the view that is being
|
||||
blurred, and it cannot contain any child components. See the [Android section](#android) for more information.
|
||||
|
||||
#### Vibrancy View
|
||||
> The vibrancy effect lets the content underneath a blurred view show through more vibrantly
|
||||
|
||||
```javascript
|
||||
const { VibrancyView } = require('react-native-blur');
|
||||
|
||||
const Menu = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<Image source={{uri}} style={styles.menu}>
|
||||
<VibrancyView blurType="light" style={styles.blur}>
|
||||
<Text>Hi, I am a tiny menu item</Text>
|
||||
</VibrancyView>
|
||||
</Image>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
> Note: `VibrancyView` is only supported on iOS. It must contain child views, otherwise the effect does not work.
|
||||
|
||||
### Component properties
|
||||
- `blurType` (String) - blur type effect
|
||||
- `xlight` - extra light blur type
|
||||
- `light` - light blur type
|
||||
- `dark` - dark blur type
|
||||
- `blurAmount` (Default: 10, Number) - blur amount effect
|
||||
- `0-100` - Adjusts blur intensity
|
||||
|
||||
### Android
|
||||
|
||||
Android support uses an [external library](https://github.com/500px/500px-android-blur) which has slightly different properties and setup requirements. This is why extra code must be added manually to the `android/app/build.gradle` file as documented above.
|
||||
|
||||
The android BlurView works by blurring an existing referenced view, so you must wait till the view you want to blur is rendered and then provide the reference to the BlurView as the `viewRef` prop. Take a look at the example to see how it works.
|
||||
|
||||
It has the following props:
|
||||
|
||||
- `viewRef` (Number) - a reference to the existing view you want to blur
|
||||
- `blurRadius` (Number)
|
||||
- `downsampleFactor` (Number)
|
||||
- `overlayColor` (Color)
|
||||
|
||||
#### Troubleshooting
|
||||
On older instances of react-native, BlurView package does not get added into the MainActivity/MainApplication classes where you would see `Warning: Native component for 'BlurView' does not exist` in RN YellowBox or console.
|
||||
|
||||
To rectify this, you can add the BlurViewPackage manually in MainActivity/MainApplication classes
|
||||
|
@ -203,5 +226,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
### Questions?
|
||||
|
||||
Feel free to contact me in [twitter](https://twitter.com/kureevalexey) or [create an issue](https://github.com/Kureev/react-native-blur/issues/new)
|
||||
|
|
|
@ -12,13 +12,16 @@ apply plugin: 'com.android.library'
|
|||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
renderscriptTargetApi 23
|
||||
renderscriptSupportModeEnabled true
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
|
@ -29,11 +32,6 @@ repositories {
|
|||
mavenCentral()
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.facebook.react:react-native:[0.32,)'
|
||||
compile 'com.fivehundredpx:blurringview:1.0.0'
|
||||
}
|
||||
|
|
|
@ -2,53 +2,10 @@ package com.cmcewen.blurview;
|
|||
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.uimanager.SimpleViewManager;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.fivehundredpx.android.blur.BlurringView;
|
||||
|
||||
|
||||
class RNBlurringView extends BlurringView {
|
||||
public RNBlurringView(ThemedReactContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlurredView(View blurredView) {
|
||||
super.setBlurredView(blurredView);
|
||||
mBlurredViewCopy = blurredView;
|
||||
checkForCircularReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
checkForCircularReference();
|
||||
}
|
||||
|
||||
private void checkForCircularReference() {
|
||||
// Need to wait until blurredView is set and the view is attached to window.
|
||||
if (mBlurredViewCopy == null || getParent() == null) return;
|
||||
|
||||
Boolean circularReference = (mBlurredViewCopy.findViewById(getId()) != null);
|
||||
if (circularReference) {
|
||||
ThemedReactContext reactContext = (ThemedReactContext) getContext();
|
||||
reactContext.getJSModule(RCTDeviceEventEmitter.class)
|
||||
.emit("ReactNativeBlurError",
|
||||
"BlurView must not be a child of the view that is being blurred.");
|
||||
|
||||
setBlurredView(null);
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Change this to protected in 500px-android-blur,
|
||||
// so we don't need our own copy.
|
||||
private View mBlurredViewCopy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class BlurViewManager extends SimpleViewManager<BlurringView> {
|
||||
|
@ -65,37 +22,38 @@ public class BlurViewManager extends SimpleViewManager<BlurringView> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RNBlurringView createViewInstance(ThemedReactContext ctx) {
|
||||
public BlurringView createViewInstance(ThemedReactContext ctx) {
|
||||
context = ctx;
|
||||
|
||||
RNBlurringView blurringView = new RNBlurringView(ctx);
|
||||
BlurringView blurringView = new BlurringView(ctx);
|
||||
blurringView.setBlurRadius(defaultRadius);
|
||||
blurringView.setDownsampleFactor(defaultSampling);
|
||||
return blurringView;
|
||||
}
|
||||
|
||||
@ReactProp(name = "blurRadius", defaultInt = defaultRadius)
|
||||
public void setRadius(RNBlurringView view, int radius) {
|
||||
public void setRadius(BlurringView view, int radius) {
|
||||
view.setBlurRadius(radius);
|
||||
view.invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "overlayColor", customType = "Color")
|
||||
public void setColor(RNBlurringView view, int color) {
|
||||
public void setColor(BlurringView view, int color) {
|
||||
view.setOverlayColor(color);
|
||||
view.invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "downsampleFactor", defaultInt = defaultSampling)
|
||||
public void setDownsampleFactor(RNBlurringView view, int factor) {
|
||||
public void setDownsampleFactor(BlurringView view, int factor) {
|
||||
view.setDownsampleFactor(factor);
|
||||
}
|
||||
|
||||
@ReactProp(name = "viewRef")
|
||||
public void setViewRef(RNBlurringView view, int viewRef) {
|
||||
public void setViewRef(BlurringView view, int viewRef) {
|
||||
View viewToBlur = context.getCurrentActivity().findViewById(viewRef);
|
||||
|
||||
if (viewToBlur != null) {
|
||||
view.setBlurredView(viewToBlur);
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
package com.cmcewen.blurview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.support.v8.renderscript.Allocation;
|
||||
import android.support.v8.renderscript.Element;
|
||||
import android.support.v8.renderscript.RenderScript;
|
||||
import android.support.v8.renderscript.ScriptIntrinsicBlur;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
|
||||
|
||||
/**
|
||||
* A custom view for presenting a dynamically blurred version of another view's content.
|
||||
* <p/>
|
||||
* Use {@link #setBlurredView(android.view.View)} to set up the reference to the view to be blurred.
|
||||
* After that, call {@link #invalidate()} to trigger blurring whenever necessary.
|
||||
*/
|
||||
public class BlurringView extends View {
|
||||
|
||||
public BlurringView(ThemedReactContext context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BlurringView(ThemedReactContext context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
final Resources res = getResources();
|
||||
final int defaultBlurRadius = res.getInteger(R.integer.default_blur_radius);
|
||||
final int defaultDownsampleFactor = res.getInteger(R.integer.default_downsample_factor);
|
||||
final int defaultOverlayColor = res.getColor(R.color.default_overlay_color);
|
||||
|
||||
initializeRenderScript(context);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PxBlurringView);
|
||||
setBlurRadius(a.getInt(R.styleable.PxBlurringView_blurRadius, defaultBlurRadius));
|
||||
setDownsampleFactor(a.getInt(R.styleable.PxBlurringView_downsampleFactor,
|
||||
defaultDownsampleFactor));
|
||||
setOverlayColor(a.getColor(R.styleable.PxBlurringView_overlayColor, defaultOverlayColor));
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setBlurredView(View blurredView) {
|
||||
mBlurredView = blurredView;
|
||||
checkForCircularReference();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (mBlurredView != null) {
|
||||
if (prepare()) {
|
||||
// If the background of the blurred view is a color drawable, we use it to clear
|
||||
// the blurring canvas, which ensures that edges of the child views are blurred
|
||||
// as well; otherwise we clear the blurring canvas with a transparent color.
|
||||
if (mBlurredView.getBackground() != null && mBlurredView.getBackground() instanceof ColorDrawable) {
|
||||
mBitmapToBlur.eraseColor(((ColorDrawable) mBlurredView.getBackground()).getColor());
|
||||
} else {
|
||||
mBitmapToBlur.eraseColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
mBlurredView.draw(mBlurringCanvas);
|
||||
blur();
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY());
|
||||
canvas.scale(mDownsampleFactor, mDownsampleFactor);
|
||||
canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
|
||||
canvas.restore();
|
||||
}
|
||||
canvas.drawColor(mOverlayColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlurRadius(int radius) {
|
||||
mBlurScript.setRadius(radius);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setDownsampleFactor(int factor) {
|
||||
if (factor <= 0) {
|
||||
throw new IllegalArgumentException("Downsample factor must be greater than 0.");
|
||||
}
|
||||
|
||||
if (mDownsampleFactor != factor) {
|
||||
mDownsampleFactor = factor;
|
||||
mDownsampleFactorChanged = true;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOverlayColor(int color) {
|
||||
if (mOverlayColor != color) {
|
||||
mOverlayColor = color;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeRenderScript(Context context) {
|
||||
mRenderScript = RenderScript.create(context);
|
||||
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
|
||||
}
|
||||
|
||||
protected boolean prepare() {
|
||||
final int width = mBlurredView.getWidth();
|
||||
final int height = mBlurredView.getHeight();
|
||||
|
||||
if (mBlurringCanvas == null || mDownsampleFactorChanged
|
||||
|| mBlurredViewWidth != width || mBlurredViewHeight != height) {
|
||||
mDownsampleFactorChanged = false;
|
||||
|
||||
mBlurredViewWidth = width;
|
||||
mBlurredViewHeight = height;
|
||||
|
||||
int scaledWidth = width / mDownsampleFactor;
|
||||
int scaledHeight = height / mDownsampleFactor;
|
||||
|
||||
// This is no longer necessary on newer versions of RenderScript (23+)
|
||||
// -------------------------------------------------------------------
|
||||
// The following manipulation is to avoid some RenderScript artifacts at the edge.
|
||||
//scaledWidth = scaledWidth - scaledWidth % 4 + 4;
|
||||
//scaledHeight = scaledHeight - scaledHeight % 4 + 4;
|
||||
|
||||
if (mBlurredBitmap == null
|
||||
|| mBlurredBitmap.getWidth() != scaledWidth
|
||||
|| mBlurredBitmap.getHeight() != scaledHeight) {
|
||||
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
if (mBitmapToBlur == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
if (mBlurredBitmap == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mBlurringCanvas = new Canvas(mBitmapToBlur);
|
||||
mBlurringCanvas.scale(1f / mDownsampleFactor, 1f / mDownsampleFactor);
|
||||
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
|
||||
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
|
||||
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void blur() {
|
||||
mBlurInput.copyFrom(mBitmapToBlur);
|
||||
mBlurScript.setInput(mBlurInput);
|
||||
mBlurScript.forEach(mBlurOutput);
|
||||
mBlurOutput.copyTo(mBlurredBitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
invalidate();
|
||||
checkForCircularReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mRenderScript != null) {
|
||||
mRenderScript.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForCircularReference() {
|
||||
// Need to wait until blurredView is set and the view is attached to window.
|
||||
if (mBlurredView == null || getParent() == null) return;
|
||||
|
||||
Boolean circularReference = (mBlurredView.findViewById(getId()) != null);
|
||||
if (circularReference) {
|
||||
ThemedReactContext reactContext = (ThemedReactContext) getContext();
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("ReactNativeBlurError",
|
||||
"BlurView must not be a child of the view that is being blurred.");
|
||||
|
||||
setBlurredView(null);
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private int mDownsampleFactor;
|
||||
private int mOverlayColor;
|
||||
|
||||
protected View mBlurredView;
|
||||
private int mBlurredViewWidth, mBlurredViewHeight;
|
||||
|
||||
private boolean mDownsampleFactorChanged;
|
||||
private Bitmap mBitmapToBlur, mBlurredBitmap;
|
||||
private Canvas mBlurringCanvas;
|
||||
private RenderScript mRenderScript;
|
||||
private ScriptIntrinsicBlur mBlurScript;
|
||||
private Allocation mBlurInput, mBlurOutput;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="PxBlurringView">
|
||||
<attr name="blurRadius" format="integer"/>
|
||||
<attr name="downsampleFactor" format="integer"/>
|
||||
<attr name="overlayColor" format="color"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="default_blur_radius">15</integer>
|
||||
<integer name="default_downsample_factor">8</integer>
|
||||
<color name="default_overlay_color">#AAFFFFFF</color>
|
||||
</resources>
|
|
@ -95,7 +95,6 @@ android {
|
|||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
|
||||
renderscriptTargetApi 23
|
||||
renderscriptSupportModeEnabled true
|
||||
}
|
||||
|
@ -130,10 +129,10 @@ android {
|
|||
|
||||
dependencies {
|
||||
compile project(':react-native-blur')
|
||||
compile project(':react-native-segmented-android')
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile "com.fivehundredpx:blurringview:1.0.0"
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Application;
|
|||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.cmcewen.blurview.BlurViewPackage;
|
||||
import com.higo.zhangyp.segmented.AndroidSegmentedPackage;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
|
@ -24,7 +25,8 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage(),
|
||||
new BlurViewPackage()
|
||||
new BlurViewPackage(),
|
||||
new AndroidSegmentedPackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -20,6 +20,5 @@ allprojects {
|
|||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
rootProject.name = 'Basic'
|
||||
include ':react-native-blur'
|
||||
project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../../../android')
|
||||
project(':react-native-blur').projectDir =
|
||||
new File(rootProject.projectDir, '../../../android')
|
||||
|
||||
include ':react-native-segmented-android', ':app'
|
||||
project(':react-native-segmented-android').projectDir =
|
||||
new File(rootProject.projectDir,'../node_modules/react-native-segmented-android')
|
||||
|
||||
include ':app'
|
||||
|
|
|
@ -11,20 +11,82 @@ import {
|
|||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
Dimensions,
|
||||
Switch,
|
||||
InteractionManager,
|
||||
} from 'react-native';
|
||||
import AndroidSegmented from 'react-native-segmented-android';
|
||||
|
||||
import { BlurView } from 'react-native-blur';
|
||||
|
||||
const BLUR_TYPES = ['xlight', 'light', 'dark'];
|
||||
|
||||
class Basic extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
viewRef: 0,
|
||||
showBlur: true,
|
||||
viewRef: null,
|
||||
activeSegment: 2,
|
||||
blurType: 'dark',
|
||||
};
|
||||
}
|
||||
|
||||
imageLoaded() {
|
||||
this.setState({ viewRef: findNodeHandle(this.refs.backgroundImage) });
|
||||
// Workaround for a tricky race condition on initial load.
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
setTimeout(() => {
|
||||
this.setState({ viewRef: findNodeHandle(this.refs.backgroundImage) });
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
_onChange(selected) {
|
||||
this.setState({
|
||||
activeSegment: selected,
|
||||
blurType: BLUR_TYPES[selected],
|
||||
});
|
||||
}
|
||||
|
||||
renderBlurView() {
|
||||
const tintColor = ['#ffffff', '#000000'];
|
||||
if (this.state.blurType === 'xlight') tintColor.reverse();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{this.state.viewRef && <BlurView
|
||||
viewRef={this.state.viewRef}
|
||||
style={styles.blurView}
|
||||
|
||||
blurRadius={9}
|
||||
blurType={this.state.blurType}
|
||||
|
||||
// The following props are also available on Android:
|
||||
|
||||
// blurRadius={20}
|
||||
// downsampleFactor={10}
|
||||
// overlayColor={'rgba(0, 0, 255, .6)'} // set a blue overlay
|
||||
/>}
|
||||
|
||||
<Text style={[styles.text, { color: tintColor[0] }]}>
|
||||
Blur component (Android)
|
||||
</Text>
|
||||
|
||||
<AndroidSegmented
|
||||
tintColor={tintColor}
|
||||
style={{
|
||||
width: Dimensions.get('window').width,
|
||||
height: 28,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
childText={BLUR_TYPES}
|
||||
orientation='horizontal'
|
||||
selectedPosition={this.state.activeSegment}
|
||||
onChange={this._onChange.bind(this)} />
|
||||
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -36,14 +98,14 @@ class Basic extends Component {
|
|||
ref={'backgroundImage'}
|
||||
onLoadEnd={this.imageLoaded.bind(this)} />
|
||||
|
||||
<BlurView
|
||||
blurRadius={15}
|
||||
downsampleFactor={5}
|
||||
overlayColor={'rgba(0, 0, 0, .3)'}
|
||||
style={styles.blurView}
|
||||
viewRef={this.state.viewRef} />
|
||||
{ this.state.showBlur ? this.renderBlurView() : null }
|
||||
|
||||
<Text style={styles.text}>Blur component</Text>
|
||||
<View
|
||||
style={styles.blurToggle}>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({showBlur: value})}
|
||||
value={this.state.showBlur} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -79,6 +141,12 @@ const styles = StyleSheet.create({
|
|||
margin: 10,
|
||||
color: '#FFFFFF',
|
||||
},
|
||||
blurToggle: {
|
||||
position: 'absolute',
|
||||
top: 30,
|
||||
right: 10,
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('Basic', () => Basic);
|
||||
|
|
|
@ -45,6 +45,8 @@ class Basic extends Component {
|
|||
}
|
||||
|
||||
renderBlurs() {
|
||||
const tintColor = this.state.blurBlurType === 'xlight' ? 'black' : 'white';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
|
||||
|
@ -57,17 +59,19 @@ class Basic extends Component {
|
|||
*/}
|
||||
<BlurView
|
||||
blurType={this.state.blurBlurType}
|
||||
blurAmount={10}
|
||||
blurAmount={100}
|
||||
style={[styles.blurView]} />
|
||||
|
||||
<Text style={styles.welcome}>Blur component</Text>
|
||||
<Text style={[styles.text, { color: tintColor }]}>
|
||||
Blur component (iOS)
|
||||
</Text>
|
||||
|
||||
<SegmentedControlIOS
|
||||
values={['xlight', 'light', 'dark']}
|
||||
selectedIndex={this.state.blurActiveSegment}
|
||||
onChange={(event) => {this._onBlurChange(event)}}
|
||||
onValueChange={(value) => {this._onBlurValueChange(value)}}
|
||||
tintColor={this.state.blurBlurType === 'xlight' ? 'black' : 'white'}
|
||||
tintColor={tintColor}
|
||||
/>
|
||||
</View>
|
||||
|
||||
|
@ -79,7 +83,11 @@ class Basic extends Component {
|
|||
blurType={this.state.vibrancyBlurType}
|
||||
blurAmount={10}
|
||||
style={[styles.container, styles.blurContainer]}>
|
||||
<Text style={styles.welcome}>Vibrancy component</Text>
|
||||
|
||||
<Text style={styles.text}>
|
||||
Vibrancy component (iOS-only)
|
||||
</Text>
|
||||
|
||||
<SegmentedControlIOS
|
||||
values={['xlight', 'light', 'dark']}
|
||||
selectedIndex={this.state.vibrancyActiveSegment}
|
||||
|
@ -143,7 +151,7 @@ const styles = StyleSheet.create({
|
|||
height: null,
|
||||
width: null,
|
||||
},
|
||||
welcome: {
|
||||
text: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"dependencies": {
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-native": "0.43.3",
|
||||
"react-native-blur": "file:../../"
|
||||
"react-native-blur": "file:../../",
|
||||
"react-native-segmented-android": "sooth-sayer/react-native-segmented-android"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "19.0.0",
|
||||
|
|
|
@ -2922,6 +2922,10 @@ react-devtools-core@^2.0.8:
|
|||
"react-native-blur@file:../../":
|
||||
version "2.0.0"
|
||||
|
||||
react-native-segmented-android@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-native-segmented-android/-/react-native-segmented-android-1.0.4.tgz#223424ab2b6b31acdd90be1ff3d49953a0df2c1d"
|
||||
|
||||
react-native@0.43.3:
|
||||
version "0.43.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.43.3.tgz#4f8e0d8d88827553789c02ef21cbc1afb68474d0"
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { View, requireNativeComponent, DeviceEventEmitter, } from 'react-native';
|
||||
|
||||
const OVERLAY_COLORS = {
|
||||
light: 'rgba(255, 255, 255, 0.2)',
|
||||
xlight: 'rgba(255, 255, 255, 0.75)',
|
||||
dark: 'rgba(16, 12, 12, 0.64)',
|
||||
};
|
||||
|
||||
|
||||
class BlurView extends Component {
|
||||
componentWillMount() {
|
||||
DeviceEventEmitter.addListener('ReactNativeBlurError', (message) => {
|
||||
|
@ -12,6 +19,35 @@ class BlurView extends Component {
|
|||
DeviceEventEmitter.removeAllListeners('ReactNativeBlurError');
|
||||
}
|
||||
|
||||
overlayColor() {
|
||||
if (this.props.overlayColor != null) return this.props.overlayColor;
|
||||
return OVERLAY_COLORS[this.props.blurType] || OVERLAY_COLORS.dark;
|
||||
}
|
||||
|
||||
blurRadius() {
|
||||
const { blurRadius, blurAmount } = this.props;
|
||||
|
||||
if (blurRadius != null) {
|
||||
if (blurRadius > 25) {
|
||||
throw new Error(`[ReactNativeBlur]: blurRadius cannot be greater than 25! (was: ${blurRadius})`);
|
||||
}
|
||||
return blurRadius;
|
||||
}
|
||||
|
||||
// iOS seems to use a slightly different blurring algorithm (or scale?).
|
||||
// Android blurRadius + downsampleFactor is approximately 80% of blurAmount.
|
||||
const equivalentBlurRadius = Math.round(blurAmount * 0.8);
|
||||
|
||||
if (equivalentBlurRadius > 25) return 25;
|
||||
return equivalentBlurRadius;
|
||||
}
|
||||
|
||||
downsampleFactor() {
|
||||
const { downsampleFactor, blurRadius } = this.props;
|
||||
if (downsampleFactor != null) return downsampleFactor;
|
||||
return blurRadius;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.children != null) {
|
||||
throw new Error(
|
||||
|
@ -20,12 +56,17 @@ class BlurView extends Component {
|
|||
'and place other views in front of it.');
|
||||
}
|
||||
|
||||
const { viewRef, style } = this.props;
|
||||
|
||||
return (
|
||||
<NativeBlurView
|
||||
{...this.props}
|
||||
style={[{
|
||||
backgroundColor: 'transparent',
|
||||
}, this.props.style,
|
||||
viewRef={viewRef}
|
||||
blurRadius={this.blurRadius()}
|
||||
downsampleFactor={this.downsampleFactor()}
|
||||
overlayColor={this.overlayColor()}
|
||||
style={[
|
||||
{ backgroundColor: 'transparent' },
|
||||
style,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
@ -34,10 +75,18 @@ class BlurView extends Component {
|
|||
|
||||
BlurView.propTypes = {
|
||||
...View.propTypes,
|
||||
blurAmount: PropTypes.number,
|
||||
blurType: PropTypes.oneOf(['dark', 'light', 'xlight']),
|
||||
|
||||
blurRadius: PropTypes.number,
|
||||
overlayColor: PropTypes.string,
|
||||
downsampleFactor: PropTypes.number,
|
||||
viewRef: PropTypes.number
|
||||
overlayColor: PropTypes.string,
|
||||
viewRef: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
BlurView.defaultProps = {
|
||||
blurType: 'dark',
|
||||
blurAmount: 10,
|
||||
};
|
||||
|
||||
const NativeBlurView = requireNativeComponent('BlurView', BlurView);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { requireNativeComponent } from 'react-native';
|
||||
import { View, requireNativeComponent } from 'react-native';
|
||||
|
||||
class BlurView extends Component {
|
||||
render() {
|
||||
return (
|
||||
<NativeBlurView
|
||||
{...this.props}
|
||||
style={[{
|
||||
backgroundColor: 'transparent',
|
||||
}, this.props.style,
|
||||
style={[
|
||||
{ backgroundColor: 'transparent' },
|
||||
this.props.style,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
@ -16,11 +16,13 @@ class BlurView extends Component {
|
|||
}
|
||||
|
||||
BlurView.propTypes = {
|
||||
blurType: PropTypes.string,
|
||||
blurAmount: PropTypes.number.isRequired,
|
||||
...View.propTypes,
|
||||
blurType: PropTypes.oneOf(['dark', 'light', 'xlight']),
|
||||
blurAmount: PropTypes.number,
|
||||
};
|
||||
|
||||
BlurView.defaultProps = {
|
||||
blurType: 'dark',
|
||||
blurAmount: 10,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue