Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement rippleRadius for TouchableNativeFeedback #1069

Merged
merged 4 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions Example/touchables/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ const TOUCHABLES = [
renderChild: renderSampleBox,
text: 'TouchableNativeFeedback (Ripple, borderless: true)',
},
{
type: TouchableNativeFeedback,
background: A => A.Ripple('blue', true, 30),
color: 'green',
renderChild: renderSampleBox,
text: 'TouchableNativeFeedback (Ripple, borderless: true, radius: 30)',
},
{
type: TouchableNativeFeedback,
background: A => A.Ripple('darkslategrey', false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import android.view.MotionEvent;
import android.view.ViewGroup;

import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ViewProps;
Expand All @@ -29,10 +31,13 @@ static class ButtonViewGroup extends ViewGroup {
int mBackgroundColor = Color.TRANSPARENT;
// Using object because of handling null representing no value set.
Integer mRippleColor;
Integer mRippleRadius;
boolean mUseForeground = false;
boolean mUseBorderless = false;
float mBorderRadius = 0;
boolean mNeedBackgroundUpdate = false;
public static final String SELECTABLE_ITEM_BACKGROUND = "selectableItemBackground";
public static final String SELECTABLE_ITEM_BACKGROUND_BORDERLESS = "selectableItemBackgroundBorderless";


public ButtonViewGroup(Context context) {
Expand All @@ -55,21 +60,31 @@ public void setRippleColor(Integer color) {
mNeedBackgroundUpdate = true;
}

public void setRippleRadius(Integer radius) {
mRippleRadius = radius;
mNeedBackgroundUpdate = true;
}

public void setBorderRadius(float borderRadius) {
mBorderRadius = borderRadius * (float)getResources().getDisplayMetrics().density;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the float cast was not needed

mBorderRadius = borderRadius * getResources().getDisplayMetrics().density;
mNeedBackgroundUpdate = true;
}

private Drawable applyRippleEffectWhenNeeded(Drawable selectable) {
if (mRippleColor != null
&& selectable != null
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selectable != null is covered by selectable instanceof RippleDrawable

&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& selectable instanceof RippleDrawable) {
int[][] states = new int[][]{ new int[]{ android.R.attr.state_enabled } };
int[] colors = new int[]{ mRippleColor };
ColorStateList colorStateList = new ColorStateList(states, colors);
((RippleDrawable) selectable).setColor(colorStateList);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& mRippleRadius != null
&& selectable instanceof RippleDrawable) {
RippleDrawable rippleDrawable = (RippleDrawable) selectable;
rippleDrawable.setRadius((int) PixelUtil.toPixelFromDIP(mRippleRadius));
}
return selectable;
}

Expand All @@ -81,10 +96,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
// We call `onTouchEvent` to and wait until button changes state to `pressed`, if it's pressed
// we return true so that the gesture handler can activate
onTouchEvent(ev);
if (isPressed()) {
return true;
}
return false;
return isPressed();
}

private void updateBackground() {
Expand Down Expand Up @@ -144,9 +156,9 @@ public void setUseBorderlessDrawable(boolean useBorderless) {

private Drawable createSelectableDrawable() {
final int version = Build.VERSION.SDK_INT;
String identifier = mUseBorderless && version >= 21 ? "selectableItemBackgroundBorderless"
: "selectableItemBackground";
int attrID = getResources().getIdentifier(identifier, "attr", "android");
String identifier = mUseBorderless && version >= 21 ? SELECTABLE_ITEM_BACKGROUND_BORDERLESS
: SELECTABLE_ITEM_BACKGROUND;
int attrID = getAttrId(getContext(), identifier);
getContext().getTheme().resolveAttribute(attrID, sResolveOutValue, true);
if (version >= 21) {
return getResources().getDrawable(sResolveOutValue.resourceId, getContext().getTheme());
Expand All @@ -155,6 +167,18 @@ private Drawable createSelectableDrawable() {
}
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static int getAttrId(Context context, String attr) {
SoftAssertions.assertNotNull(attr);
if (SELECTABLE_ITEM_BACKGROUND.equals(attr)) {
return android.R.attr.selectableItemBackground;
} else if (SELECTABLE_ITEM_BACKGROUND_BORDERLESS.equals(attr)) {
return android.R.attr.selectableItemBackgroundBorderless;
} else {
return context.getResources().getIdentifier(attr, "attr", "android");
}
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// No-op
Expand Down Expand Up @@ -225,6 +249,11 @@ public void setRippleColor(ButtonViewGroup view, Integer rippleColor) {
view.setRippleColor(rippleColor);
}

@ReactProp(name = "rippleRadius")
public void setRippleRadius(ButtonViewGroup view, Integer rippleColor) {
view.setRippleRadius(rippleColor);
}

@Override
protected void onAfterUpdateTransaction(ButtonViewGroup view) {
view.updateBackground();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) {
mOrchestrator.onTouchEvent(ev);
mPassingTouch = false;

if (mShouldIntercept) {
return true;
} else {
return false;
}
return mShouldIntercept;
}

private void tryCancelAllHandlers() {
Expand Down
17 changes: 12 additions & 5 deletions touchables/TouchableNativeFeedback.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ import GenericTouchable from './GenericTouchable';
* it follows native behaviours.
*/
export default class TouchableNativeFeedback extends Component {
static SelectableBackground = () => ({ type: 'SelectableBackground' });
static SelectableBackgroundBorderless = () => ({
static SelectableBackground = rippleRadius => ({
type: 'SelectableBackground',
rippleRadius,
});
static SelectableBackgroundBorderless = rippleRadius => ({
type: 'SelectableBackgroundBorderless',
rippleRadius,
});
static Ripple = (color, borderless) => ({
static Ripple = (color, borderless, rippleRadius) => ({
type: 'Ripple',
color,
borderless,
rippleRadius,
});

static canUseNativeForeground = () => Platform.Version >= 23;
Expand All @@ -38,20 +43,22 @@ export default class TouchableNativeFeedback extends Component {
style: PropTypes.any,
};

getExtraButtonProps = () => {
getExtraButtonProps() {
const extraProps = {};
const { background } = this.props;
if (background) {
if (background.type === 'Ripple') {
extraProps['borderless'] = background.borderless;
extraProps['rippleColor'] = background.color;
extraProps['rippleRadius'] = background.rippleRadius;
} else if (background.type === 'SelectableBackgroundBorderless') {
extraProps['borderless'] = true;
extraProps['rippleRadius'] = background.rippleRadius;
}
}
extraProps['foreground'] = this.props.useForeground;
return extraProps;
};
}
render() {
const { style = {}, ...rest } = this.props;
return (
Expand Down