Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Android] Button Border can be set independent of other properties and will not change the size of the Button. **behavior change** #1570

Merged
merged 26 commits into from
Feb 8, 2018

Conversation

samhouts
Copy link
Member

@samhouts samhouts commented Jan 12, 2018

Description of Change

This fix reverts #1178 and restores the changes made in #941, which will allow Android Buttons to have a border without a BorderRadius specified.

It goes further to allow a border to be added and/or border radius to be changed without changing the BackgroundColor of the Button.

It also keeps the size of the Button the same as a default Material button, draws a shadow, and preserves the "ripple" effect when the Button is pressed.

It does not preserve the state list animation that causes the Button to slowly fade to the pressed color and increase elevation when long pressed. That can be added in a future PR if necessary

This PR has a potential breaking behavior change. It hardcodes the default color of the button on pre-AppCompat because the theme color does not seem to match the actual color that is used.

We have also marked Button.BorderRadius as obsolete in favor of Button.CornerRadius. The BorderRadius property will forward to the CornerRadius property.

Before (AppCompat):
before

Before (pre-AppCompat):
before-preappc

Note: The test description lies on pre-AppCompat, since the default Button color is actually darker than the color that the "darker" button is. Apparently, the definition of Color.Gray is different.

with #941 (AppCompat):
before-with-941

with #941 (pre-AppCompat):
before-with-941-preappc

After (AppCompat):
after

After (pre-AppCompat):
after-preappc

Note that the shadow is a lighter color of the BackgroundColor, matching the pressed color of the Button. When a blue background is paired with a red border color, it looks a bit odd, like the Button is expanding past the border. However, it looks fine with a sensible color combination, as seen with the gray Button with red border.

Bugs Fixed

API Changes

Deprecated (note, marked obsolete but still present and functional):

- [Obsolete] Button.BorderRadius
- [Obsolete] Button.BorderRadiusProperty

Added:

- Button.CornerRadius
- Button.CornerRadiusProperty

Behavioral Changes

  • Button.BorderRadius is now obsolete. Please use Button.CornerRadius now.
  • Default value of Button.BackgroundColor is now hardcoded on pre-AppCompat instead of derived from a theme resource.
  • Shadow color in Pre-AppCompat is slightly lighter when BorderRadius/BorderColor/BorderWidth are specified.

PR Checklist

  • Has tests (if omitted, state reason in description)
  • Rebased on top of master at time of PR
  • Changes adhere to coding standard
  • Consolidate commits as makes sense

@samhouts samhouts added the breaking Changes behavior or appearance label Jan 12, 2018
@samhouts samhouts added DO-NOT-MERGE-!!! 🛑 This is in progress and needs to be updated before it can be merged. and removed DO-NOT-MERGE-!!! 🛑 This is in progress and needs to be updated before it can be merged. labels Jan 24, 2018
@StephaneDelcroix
Copy link
Member

@samhouts to summarize the discussion we had yesterday, I'd recommend not using a defaultValueCreator, but using a default value of -1 meaning whatever-the-default-is-for-that-control-on-this-platform.

to mitigate the breaking change, you could e.g. deprecate BorderRadius in favor of CornerRadius (shared with Frame).

@samhouts samhouts changed the title [Android] Button Border can be set independent of other propertes and will not change the size of the Button. fixes #1436 **behavior change** [Android] Button Border can be set independent of other properties and will not change the size of the Button. fixes #1436 **behavior change** Feb 2, 2018
Copy link
Member

@StephaneDelcroix StephaneDelcroix left a comment

Choose a reason for hiding this comment

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

I reviewed the Core part, see comments.

I understand why you added DefaultValue fields, for avoiding comparing against magic hardcoded numbers in renderers. Actually, the BindableProperty.DefaultValue serves that purposes, no need for the extra field.

If you prefer not doing the change for CSS support in this PR, that's fine. I can do it in another one as this one is merged.

Overall, it's 👍

public const int DefaultCornerRadius = -1;
public const double DefaultBorderWidth = -1;
public static Color DefaultBorderColor = Color.Default;
public static Color DefaultTextColor = Color.Default;
Copy link
Member

Choose a reason for hiding this comment

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

those 2 aren't magic. they do not require fields.

@@ -31,32 +39,42 @@ public class Button : View, IFontElement, ITextElement, IButtonController, IElem

public static readonly BindableProperty FontAttributesProperty = FontElement.FontAttributesProperty;

public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create("BorderWidth", typeof(double), typeof(Button), -1d);
public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create("BorderWidth", typeof(double), typeof(Button), DefaultBorderWidth);
Copy link
Member

Choose a reason for hiding this comment

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

I don't see the need to introduce a level of indirection here. one can check the default value by doing BorderWidthProperty.GetDefaultValue()

@@ -7,6 +7,8 @@ namespace Xamarin.Forms
{
public partial class VisualElement : Element, IAnimatable, IVisualElementController, IResourcesProvider
{
public static Color DefaultBackgroundColor = Color.Default;
Copy link
Member

Choose a reason for hiding this comment

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

same

public static readonly BindableProperty BorderRadiusProperty = BindableProperty.Create("BorderRadius", typeof(int), typeof(Button), defaultValue: DefaultBorderRadius,
propertyChanged: BorderRadiusPropertyChanged);

public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create("CornerRadius", typeof(int), typeof(Button), defaultValue: DefaultCornerRadius,
Copy link
Member

Choose a reason for hiding this comment

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

can we promote CornerRadius to BorderElement (shared with Frame) so we could add the border-radius CSS property targeting both ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm targeting 15-5, so that's not in this branch. We can add it when it goes to master. :)

#pragma warning disable 0618 // retain until BorderRadiusProperty removed
bindable.SetValue(Button.BorderRadiusProperty, val);
#pragma warning restore
}
Copy link
Member

Choose a reason for hiding this comment

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

I'm trying to think about all the things that could go wrong here:

  • binding => should be fine, unless user binds to the 2 properties and has a poor INPC implementation in VM
  • clearvalue => could you please add a unit test for button making sure that when a property is Cleared (using ClearValue both are actually cleared ? i.e. IsSet() == false; ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Unit tests added! However, it looks like we don't clear the context when we call ClearValue, so IsSet returns true in that case. Added a test to BindableObject to show that it's not specific to this case.

Copy link
Member

Choose a reason for hiding this comment

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

😢

@StephaneDelcroix
Copy link
Member

Is this still [breaking] and a behavior change ?

bindable.ClearValue(bindableProperty);

var isSet = bindable.IsSet(bindableProperty);
Assert.IsTrue(isSet);
Copy link
Member

Choose a reason for hiding this comment

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

I'll look at fixing this, or investigating why it is that way. if ClearValue is dropping the context, you could ClearValue the other property when the first is !IsSet() in your onPropertyChanged

@samhouts samhouts added the DO-NOT-MERGE-!!! 🛑 This is in progress and needs to be updated before it can be merged. label Feb 6, 2018
@samhouts
Copy link
Member Author

samhouts commented Feb 7, 2018

Since we touched the Core code, we need to ensure that we didn't break iOS or UWP with this change. Here's proof:

iOS: Before
io-before

iOS: After
io-after

UWP Before:
uwp-before

UWP After
uwp-after

@samhouts samhouts added breaking Changes behavior or appearance and removed breaking Changes behavior or appearance DO-NOT-MERGE-!!! 🛑 This is in progress and needs to be updated before it can be merged. labels Feb 7, 2018
Copy link
Member

@rmarinho rmarinho left a comment

Choose a reason for hiding this comment

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

Great work.

@rmarinho rmarinho merged commit 2fba186 into 15-5 Feb 8, 2018
@samhouts samhouts changed the title [Android] Button Border can be set independent of other properties and will not change the size of the Button. fixes #1436 **behavior change** [Android] Button Border can be set independent of other properties and will not change the size of the Button. **behavior change** Feb 12, 2018
@samhouts samhouts deleted the fix-issue1436 branch February 15, 2018 17:52
@bill2004158
Copy link
Contributor

How can I disable shadow for the button which I don't need it?

@bill2004158
Copy link
Contributor

it seems the new version added some Padding for the button?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants