Last active
December 20, 2018 23:21
-
-
Save PureWeen/855e7acdffe2087553a75881f95d23f6 to your computer and use it in GitHub Desktop.
Border Drawable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using Android.Graphics; | |
using Android.Graphics.Drawables; | |
using AColor = Android.Graphics.Color; | |
namespace Xamarin.Forms.Platform.Android | |
{ | |
internal class BorderDrawable : Drawable | |
{ | |
public const int DefaultCornerRadius = 2; // Default value for Android material button. | |
readonly Func<double, float> _convertToPixels; | |
bool _isDisposed; | |
Bitmap _normalBitmap; | |
bool _pressed; | |
Bitmap _pressedBitmap; | |
float _paddingLeft; | |
float _paddingTop; | |
Color _defaultColor; | |
readonly bool _drawOutlineWithBackground; | |
AColor _shadowColor; | |
float _shadowDx; | |
float _shadowDy; | |
float _shadowRadius; | |
float PaddingLeft | |
{ | |
get { return (_paddingLeft / 2f) + _shadowDx; } | |
set { _paddingLeft = value; } | |
} | |
float PaddingTop | |
{ | |
get { return (_paddingTop / 2f) + _shadowDy; } | |
set { _paddingTop = value; } | |
} | |
double BorderWidth => Math.Max(BorderElement.IsBorderWidthSet() ? BorderElement.BorderWidth : .25, 0); | |
Color BorderColor => BorderElement.IsBorderColorSet() && BorderElement.BorderColor != Color.Default ? BorderElement.BorderColor : Color.FromHex("#c5c5c5"); | |
public BorderDrawable(Func<double, float> convertToPixels, Color defaultColor, bool drawOutlineWithBackground) | |
{ | |
_convertToPixels = convertToPixels; | |
_pressed = false; | |
_defaultColor = defaultColor; | |
_drawOutlineWithBackground = drawOutlineWithBackground; | |
} | |
public IBorderElement BorderElement | |
{ | |
get; | |
set; | |
} | |
public override bool IsStateful | |
{ | |
get { return true; } | |
} | |
public override int Opacity | |
{ | |
get { return 0; } | |
} | |
public override void Draw(Canvas canvas) | |
{ | |
//Bounds = new Rect(Bounds.Left, Bounds.Top, Bounds.Right + (int)_convertToPixels(10), Bounds.Bottom + (int)_convertToPixels(10)); | |
int width = Bounds.Width(); | |
int height = Bounds.Height(); | |
if (width <= 0 || height <= 0) | |
return; | |
if (_normalBitmap == null || | |
_normalBitmap?.IsDisposed() == true || | |
_pressedBitmap?.IsDisposed() == true || | |
_normalBitmap.Height != height || | |
_normalBitmap.Width != width) | |
Reset(); | |
if (!_drawOutlineWithBackground && BorderElement.BackgroundColor == Color.Default) | |
return; | |
Bitmap bitmap = null; | |
if (GetState().Contains(global::Android.Resource.Attribute.StatePressed)) | |
{ | |
_pressedBitmap = _pressedBitmap ?? CreateBitmap(true, width, height); | |
bitmap = _pressedBitmap; | |
} | |
else | |
{ | |
_normalBitmap = _normalBitmap ?? CreateBitmap(false, width, height); | |
bitmap = _normalBitmap; | |
} | |
canvas.DrawBitmap(bitmap, 0, 0, new Paint()); | |
} | |
public BorderDrawable SetShadow(float dy, float dx, AColor color, float radius) | |
{ | |
_shadowDx = dx; | |
_shadowDy = dy; | |
_shadowColor = color; | |
_shadowRadius = radius; | |
return this; | |
} | |
public BorderDrawable SetPadding(float top, float left) | |
{ | |
_paddingTop = top; | |
_paddingLeft = left; | |
return this; | |
} | |
public void Reset() | |
{ | |
if (_normalBitmap != null) | |
{ | |
if (!_normalBitmap.IsDisposed()) | |
{ | |
_normalBitmap.Recycle(); | |
_normalBitmap.Dispose(); | |
} | |
_normalBitmap = null; | |
} | |
if (_pressedBitmap != null) | |
{ | |
if (!_pressedBitmap.IsDisposed()) | |
{ | |
_pressedBitmap.Recycle(); | |
_pressedBitmap.Dispose(); | |
} | |
_pressedBitmap = null; | |
} | |
} | |
public override void SetAlpha(int alpha) | |
{ | |
} | |
public override void SetColorFilter(ColorFilter cf) | |
{ | |
} | |
public Color BackgroundColor => BorderElement.BackgroundColor == Color.Default ? _defaultColor : BorderElement.BackgroundColor; | |
public Color PressedBackgroundColor => BackgroundColor.AddLuminosity(-.12);//<item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item> | |
protected override void Dispose(bool disposing) | |
{ | |
if (_isDisposed) | |
return; | |
_isDisposed = true; | |
if (disposing) | |
Reset(); | |
base.Dispose(disposing); | |
} | |
protected override bool OnStateChange(int[] state) | |
{ | |
bool old = _pressed; | |
_pressed = state.Contains(global::Android.Resource.Attribute.StatePressed); | |
if (_pressed != old) | |
{ | |
InvalidateSelf(); | |
return true; | |
} | |
return false; | |
} | |
Bitmap CreateBitmap(bool pressed, int width, int height) | |
{ | |
Bitmap bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888); | |
using (var canvas = new Canvas(bitmap)) | |
{ | |
DrawBackground(canvas, width, height, pressed); | |
if (_drawOutlineWithBackground) | |
DrawOutline(canvas, width, height); | |
} | |
return bitmap; | |
} | |
float ConvertCornerRadiusToPixels() | |
{ | |
int cornerRadius = DefaultCornerRadius; | |
if (BorderElement.IsCornerRadiusSet() && BorderElement.CornerRadius != (int)BorderElement.CornerRadiusDefaultValue) | |
cornerRadius = BorderElement.CornerRadius; | |
return _convertToPixels(cornerRadius); | |
} | |
public RectF GetPaddingBounds(int width, int height) | |
{ | |
RectF rect = new RectF(0, 0, width, height); | |
rect.Inset(PaddingLeft, PaddingTop); | |
return rect; | |
} | |
RectF createRect(int width, int height) | |
{ | |
RectF rect = new RectF(0, 0, width, height); | |
rect.Inset(PaddingLeft , PaddingTop); | |
return rect; | |
} | |
RectF createRectOutline(int width, int height) | |
{ | |
RectF rect = new RectF(0, 0, width, height); | |
float borderWidth = _convertToPixels(BorderWidth); | |
rect.Inset(PaddingLeft, PaddingTop); | |
rect.Inset(borderWidth / 2, borderWidth / 2); | |
return rect; | |
} | |
void DrawBackground(Canvas canvas, int width, int height, bool pressed) | |
{ | |
using (var paint = new Paint { AntiAlias = true }) | |
using (var path = new Path()) | |
{ | |
float borderRadius = Math.Max(ConvertCornerRadiusToPixels(), 0); | |
RectF rect = createRect(width, height); | |
float borderWidth = _convertToPixels(BorderWidth); | |
float inset = borderWidth / 2; | |
path.AddRoundRect(rect, borderRadius, borderRadius, Path.Direction.Cw); | |
paint.Color = pressed ? PressedBackgroundColor.ToAndroid() : BackgroundColor.ToAndroid(); | |
paint.SetStyle(Paint.Style.Fill); | |
paint.SetShadowLayer(_shadowRadius, _shadowDx, _shadowDy, _shadowColor); | |
canvas.DrawPath(path, paint); | |
} | |
} | |
public void DrawOutline(Canvas canvas, int width, int height) | |
{ | |
using (var paint = new Paint { AntiAlias = true }) | |
{ | |
float borderWidth = _convertToPixels(BorderElement.BorderWidth); | |
// adjust border radius so outer edge of stroke is same radius as border radius of background | |
float borderRadius = Math.Max(ConvertCornerRadiusToPixels(), 0); | |
borderRadius = (float)borderRadius - (float)borderWidth / 2.0F; | |
RectF rect = createRectOutline(width, height); | |
paint.StrokeWidth = borderWidth; | |
paint.SetStyle(Paint.Style.Stroke); | |
paint.Color = BorderColor.ToAndroid(); | |
canvas.DrawRoundRect(rect, borderRadius, borderRadius, paint); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment