Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import Slider from '@react-native-community/slider';
maximumValue={1}
minimumTrackTintColor="#FFFFFF"
maximumTrackTintColor="#000000"
thumbSize={32}
/>
```

Expand Down Expand Up @@ -93,6 +94,7 @@ To use this library you need to ensure you are using the correct version of Reac
| `inverted` | Reverses the direction of the slider.<br/>Default value is false. | bool | |
| `vertical` | Changes the orientation of the slider to vertical, if set to `true`.<br/>Default value is false. | bool | Windows |
| `thumbTintColor` | Color of the foreground switch grip.<br/>**NOTE:** This prop will override the `thumbImage` prop set, meaning that if both `thumbImage` and `thumbTintColor` will be set, image used for the thumb may not be displayed correctly! | [color](https://reactnative.dev/docs/colors) | Android |
| `thumbSize` | Sets the size (width and height) of the thumb.<br/>If `thumbImage` is provided, it will be scaled to this size.<br/>Units: points on iOS, dp on Android. | number | Android, iOS, Web |
| `maximumTrackImage` | Assigns a maximum track image. Only static images are supported. The leftmost pixel of the image will be stretched to fill the track. | Image<br/>.propTypes<br/>.source | iOS |
| `minimumTrackImage` | Assigns a minimum track image. Only static images are supported. The rightmost pixel of the image will be stretched to fill the track. | Image<br/>.propTypes<br/>.source | iOS |
| `thumbImage` | Sets an image for the thumb. Only static images are supported. Needs to be a URI of a local or network image; base64-encoded SVG is not supported. | Image<br/>.propTypes<br/>.source | |
Expand Down
17 changes: 17 additions & 0 deletions example/src/Examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -744,4 +744,21 @@ export const examples: Props[] = [
return <SliderExample disabled value={0.6} />;
},
},
{
title: 'Custom thumb size (no image)',
render() {
return <SliderExample thumbTintColor={'blue'} thumbSize={32} />;
},
},
{
title: 'Custom thumb size (scaled image)',
render() {
return (
<SliderExample
thumbImage={require('./resources/uie_thumb_big.png')}
thumbSize={60}
/>
);
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.Log;
Expand Down Expand Up @@ -74,6 +77,15 @@ public class ReactSlider extends AppCompatSeekBar {
/** Upper limit based on the SeekBar progress 0..total steps */
private int mUpperLimit;

/** Thumb size in pixels (0 = default) */
private int mThumbSizePx = 0;

/** Original thumb drawable URI */
@Nullable private String mThumbImageUri = null;

/** Cached thumb tint color */
@Nullable private Integer mThumbTintColor = null;

public ReactSlider(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
Expand Down Expand Up @@ -299,15 +311,88 @@ public BitmapDrawable call() {
return bitmapDrawable;
}

public void setThumbImage(final String uri) {
if (uri != null) {
setThumb(getBitmapDrawable(uri));
// Enable alpha channel for the thumbImage
public void setThumbImage(@Nullable final String uri) {
mThumbImageUri = uri;
updateThumbImage();
}

public void setThumbSize(final double size) {
float density = getResources().getDisplayMetrics().density;
mThumbSizePx = size > 0 ? Math.round((float) size * density) : 0;
updateThumbImage();
}

public void setThumbTintColor(@Nullable final Integer color) {
mThumbTintColor = color;
if (mThumbImageUri != null || mThumbSizePx > 0) {
updateThumbImage();
} else {
applyThumbTintColorFilter();
}
}

private void applyThumbTintColorFilter() {
if (getThumb() == null) {
return;
}

if (mThumbTintColor != null) {
getThumb().setColorFilter(mThumbTintColor, PorterDuff.Mode.SRC_IN);
} else {
getThumb().clearColorFilter();
}
}

private void updateThumbImage() {
if (mThumbImageUri != null) {
BitmapDrawable drawable = getBitmapDrawable(mThumbImageUri);
if (drawable != null) {
if (mThumbSizePx > 0) {
Bitmap originalBitmap = drawable.getBitmap();
Bitmap scaledBitmap =
Bitmap.createScaledBitmap(originalBitmap, mThumbSizePx, mThumbSizePx, true);
setThumb(new BitmapDrawable(getResources(), scaledBitmap));
} else {
setThumb(drawable);
}
applyThumbTintColorFilter();
// Enable alpha channel for the thumbImage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSplitTrack(false);
}
return;
}
}

if (mThumbSizePx > 0) {
Bitmap bitmap = Bitmap.createBitmap(mThumbSizePx, mThumbSizePx, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);

int fillColor =
mThumbTintColor != null
? mThumbTintColor
: (getThumbTintList() != null ? getThumbTintList().getDefaultColor() : 0xFFFFFFFF);

Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
fillPaint.setStyle(Paint.Style.FILL);
fillPaint.setColor(fillColor);
float radius = mThumbSizePx / 2f;
canvas.drawCircle(radius, radius, radius, fillPaint);

Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(1);
strokePaint.setColor(0x1A000000);
canvas.drawCircle(radius, radius, radius - 0.5f, strokePaint);

setThumb(new BitmapDrawable(getResources(), bitmap));
applyThumbTintColorFilter();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSplitTrack(false);
}
} else {
setThumb(getThumb());
// No special sizing; keep existing thumb, only apply tint if needed.
applyThumbTintColorFilter();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ public void setThumbTintColor(ReactSlider view, Integer color) {
ReactSliderManagerImpl.setThumbTintColor(view, color);
}

@Override
@ReactProp(name = "thumbSize", defaultFloat = 0f)
public void setThumbSize(ReactSlider view, double size) {
ReactSliderManagerImpl.setThumbSize(view, size);
}

@Override
@ReactProp(name = "minimumTrackTintColor", customType = "Color")
public void setMinimumTrackTintColor(ReactSlider view, Integer color) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,7 @@ public static void setDisabled(ReactSlider view, boolean disabled) {
}

public static void setThumbTintColor(ReactSlider view, Integer color) {
if (view.getThumb() != null) {
if (color == null) {
view.getThumb().clearColorFilter();
} else {
view.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
view.setThumbTintColor(color);
}

public static void setMinimumTrackTintColor(ReactSlider view, Integer color) {
Expand All @@ -101,6 +95,10 @@ public static void setThumbImage(ReactSlider view, @Nullable ReadableMap source)
view.setThumbImage(uri);
}

public static void setThumbSize(ReactSlider view, double size) {
view.setThumbSize(size);
}

public static void setMaximumTrackTintColor(ReactSlider view, Integer color) {
LayerDrawable drawable = (LayerDrawable) view.getProgressDrawable().getCurrent();
Drawable background = drawable.findDrawableByLayerId(android.R.id.background);
Expand Down
2 changes: 2 additions & 0 deletions package/ios/RNCSlider.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
@property (nonatomic, strong) UIImage *minimumTrackImage;
@property (nonatomic, strong) UIImage *maximumTrackImage;
@property (nonatomic, strong) UIImage *thumbImage;
@property (nonatomic, assign) CGFloat thumbSize;
@property (nonatomic, assign) bool tapToSeek;
@property (nonatomic, strong) NSString *accessibilityUnits;
@property (nonatomic, strong) NSArray *accessibilityIncrements;

- (float) discreteValue:(float)value;
- (void) setDisabled:(bool)disabled;
- (void) updateThumbImage;

@end
47 changes: 46 additions & 1 deletion package/ios/RNCSlider.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ @implementation RNCSlider
float _unclippedValue;
bool _minimumTrackImageSet;
bool _maximumTrackImageSet;
UIImage *_thumbImage;
CGFloat _thumbSize;
}

- (instancetype)init {
Expand Down Expand Up @@ -116,14 +118,57 @@ - (UIImage *)maximumTrackImage

- (void)setThumbImage:(UIImage *)thumbImage
{
[self setThumbImage:thumbImage forState:UIControlStateNormal];
_thumbImage = thumbImage;
[self updateThumbImage];
}

- (UIImage *)thumbImage
{
return [self thumbImageForState:UIControlStateNormal];
}

- (void)setThumbSize:(CGFloat)thumbSize
{
_thumbSize = thumbSize;
[self updateThumbImage];
}

- (void)updateThumbImage
{
UIImage *imageToSet = nil;

if (_thumbSize > 0) {
CGSize newSize = CGSizeMake(_thumbSize, _thumbSize);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();

if (_thumbImage) {
[_thumbImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
} else {
UIColor *fillColor = self.thumbTintColor ?: [UIColor whiteColor];
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0, 0, newSize.width, newSize.height));

CGContextSetStrokeColorWithColor(context, [[UIColor colorWithWhite:0.0 alpha:0.1] CGColor]);
CGContextSetLineWidth(context, 0.5);
CGContextStrokeEllipseInRect(
context,
CGRectMake(0.25, 0.25, newSize.width - 0.5, newSize.height - 0.5));
}

imageToSet = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} else if (_thumbImage) {
imageToSet = _thumbImage;
}

if (imageToSet) {
[self setThumbImage:imageToSet forState:UIControlStateNormal];
[self setThumbImage:imageToSet forState:UIControlStateHighlighted];
[self setThumbImage:imageToSet forState:UIControlStateSelected];
}
}

- (void)setInverted:(BOOL)inverted
{
if (inverted) {
Expand Down
1 change: 1 addition & 0 deletions package/ios/RNCSliderComponentView.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef void (^RNCLoadImageFailureBlock)();
@property (nonatomic, strong) UIImage *minimumTrackImage;
@property (nonatomic, strong) UIImage *maximumTrackImage;
@property (nonatomic, strong) UIImage *thumbImage;
@property (nonatomic, assign) CGFloat thumbSize;
@property (nonatomic, assign) bool tapToSeek;
@property (nonatomic, strong) NSString *accessibilityUnits;
@property (nonatomic, strong) NSArray *accessibilityIncrements;
Expand Down
6 changes: 6 additions & 0 deletions package/ios/RNCSliderComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
if (oldScreenProps.thumbTintColor != newScreenProps.thumbTintColor) {
slider.thumbTintColor = RCTUIColorFromSharedColor(newScreenProps.thumbTintColor);
}
if (oldScreenProps.thumbSize != newScreenProps.thumbSize) {
slider.thumbSize = newScreenProps.thumbSize;
}
if (oldScreenProps.minimumTrackTintColor != newScreenProps.minimumTrackTintColor) {
slider.minimumTrackTintColor = RCTUIColorFromSharedColor(newScreenProps.minimumTrackTintColor);
}
Expand Down Expand Up @@ -236,6 +239,9 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
[self->slider setThumbImage:nil];
}];
}
if (oldScreenProps.thumbTintColor != newScreenProps.thumbTintColor && slider.thumbSize > 0) {
[slider updateThumbImage];
}
if (oldScreenProps.trackImage != newScreenProps.trackImage) {
[self loadImageFromImageSource:newScreenProps.trackImage completionBlock:^(NSError *error, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
5 changes: 5 additions & 0 deletions package/src/RNCSliderNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface NativeProps extends ViewProps {
testID?: string;
thumbImage?: ImageSource;
thumbTintColor?: ColorValue;
/**
* Sets the size (width and height) of the thumb, in points (dp on Android).
* If you also set `thumbImage`, the image will be scaled to this size.
*/
thumbSize?: Double;
trackImage?: ImageSource;
value?: Float;
lowerLimit?: Float;
Expand Down
7 changes: 7 additions & 0 deletions package/src/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ type Props = ViewProps &
*/
thumbImage?: ImageSource;

/**
* Sets the size (width and height) of the thumb.
* If `thumbImage` is provided, it will be scaled to this size.
*/
thumbSize?: number;

/**
* If true the slider will be inverted.
* Default value is false.
Expand Down Expand Up @@ -340,6 +346,7 @@ const SliderComponent = (
? 'transparent'
: props.thumbTintColor
}
thumbSize={props.thumbSize}
/>
</View>
);
Expand Down
12 changes: 12 additions & 0 deletions package/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export interface SliderPropsIOS extends ReactNative.ViewProps {
*/
thumbImage?: ReactNative.ImageURISource;

/**
* Sets the size (width and height) of the thumb.
* If `thumbImage` is provided, it will be scaled to this size.
*/
thumbSize?: number;

/**
* Assigns a single image for the track. Only static images
* are supported. The center pixel of the image will be stretched
Expand Down Expand Up @@ -166,6 +172,12 @@ export interface SliderProps
*/
inverted?: boolean;

/**
* Sets the size (width and height) of the thumb.
* If `thumbImage` is provided, it will be scaled to this size.
*/
thumbSize?: number;

/**
* Component to be rendered for each step indicator.
*/
Expand Down