안녕하세요.
영화 자막 처럼 표현하는 방법을 알고 싶습니다.
현재는 Avalonia 11.0.10을 사용하는 중이며, 을 이용하여 그림자 효과를 주어 비슷하게 표현을 해주었습니다.
다만, 아무래도 영화 자막 처럼 검은색 테두리가 뚜렷하게 나오지는 않네요.
방법이 있다면 조언좀 부탁드립니다.
감사합니다~
안녕하세요.
영화 자막 처럼 표현하는 방법을 알고 싶습니다.
현재는 Avalonia 11.0.10을 사용하는 중이며, 을 이용하여 그림자 효과를 주어 비슷하게 표현을 해주었습니다.
다만, 아무래도 영화 자막 처럼 검은색 테두리가 뚜렷하게 나오지는 않네요.
방법이 있다면 조언좀 부탁드립니다.
감사합니다~
흰색글씨에 검은색 테두리 글씨 말씀하시는건가용?
예압~ 제가 원하는 것 입니다.
없다면야 만들어겠지만…ㅠㅠ 지금 만드는 것도 생각 중이에요 ㅎㅎ
글자가 잘렸네요 DropShadowDirectionEffect을 이용해서 그림자 효과를 표현했습니다
아래 링크를 참고했습니다.
해당 링크의 결과는 테두리가 글자를 덮는 방식이라 다시 구현해봤습니다.
사용법
<local:OutlinedTextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Stroke="Black"
StrokeThickness="5"
FontFamily="a시네마M"
Text="영화 자막같은 테두리"
FontSize="30"
Foreground="#FFFFFF" />
코드
using System;
using System.Threading;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Media;
namespace MyProject;
public class OutlinedTextBlock : Control
{
/// <summary>
/// Defines the <see cref="Text"/> property.
/// </summary>
public static readonly StyledProperty<string> TextProperty =
AvaloniaProperty.Register<OutlinedTextBlock, string>(nameof(Text));
/// <summary>
/// Defines the <see cref="FontFamily"/> property.
/// </summary>
public static readonly StyledProperty<FontFamily> FontFamilyProperty =
TextElement.FontFamilyProperty.AddOwner<OutlinedTextBlock>();
/// <summary>
/// Defines the <see cref="FontSize"/> property.
/// </summary>
public static readonly StyledProperty<double> FontSizeProperty =
TextElement.FontSizeProperty.AddOwner<OutlinedTextBlock>();
/// <summary>
/// Defines the <see cref="FontStyle"/> property.
/// </summary>
public static readonly StyledProperty<FontStyle> FontStyleProperty =
TextElement.FontStyleProperty.AddOwner<OutlinedTextBlock>();
/// <summary>
/// Defines the <see cref="FontWeight"/> property.
/// </summary>
public static readonly StyledProperty<FontWeight> FontWeightProperty =
TextElement.FontWeightProperty.AddOwner<OutlinedTextBlock>();
/// <summary>
/// Defines the <see cref="Foreground"/> property.
/// </summary>
public static readonly StyledProperty<IBrush> ForegroundProperty =
TextElement.ForegroundProperty.AddOwner<OutlinedTextBlock>();
/// <summary>
/// Defines the <see cref="Stroke"/> property.
/// </summary>
public static readonly StyledProperty<IBrush> StrokeProperty =
AvaloniaProperty.Register<OutlinedTextBlock, IBrush>(nameof(Stroke));
/// <summary>
/// Defines the <see cref="StrokeThickness"/> property.
/// </summary>
public static readonly StyledProperty<double> StrokeThicknessProperty =
AvaloniaProperty.Register<OutlinedTextBlock, double>(nameof(StrokeThickness));
private IPen _strokePen;
private Geometry _textGeometry;
static OutlinedTextBlock()
{
AffectsGeometry<OutlinedTextBlock>(
BoundsProperty,
TextProperty,
FontSizeProperty,
FontWeightProperty,
FontStyleProperty,
StrokeThicknessProperty);
}
/// <summary>
/// Gets or sets the text.
/// </summary>
public string Text
{
get => GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
/// <summary>
/// Gets or sets the text.
/// </summary>
public IBrush Stroke
{
get => GetValue(StrokeProperty);
set => SetValue(StrokeProperty, value);
}
/// <summary>
/// Gets or sets the text.
/// </summary>
public double StrokeThickness
{
get => GetValue(StrokeThicknessProperty);
set => SetValue(StrokeThicknessProperty, value);
}
/// <summary>
/// Gets or sets the font family used to draw the control's text.
/// </summary>
public FontFamily FontFamily
{
get => GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
/// <summary>
/// Gets or sets the font family used to draw the control's text.
/// </summary>
public IBrush Foreground
{
get => GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
/// <summary>
/// Gets or sets the size of the control's text in points.
/// </summary>
public double FontSize
{
get => GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
/// <summary>
/// Gets or sets the font style used to draw the control's text.
/// </summary>
public FontStyle FontStyle
{
get => GetValue(FontStyleProperty);
set => SetValue(FontStyleProperty, value);
}
/// <summary>
/// Gets or sets the font weight used to draw the control's text.
/// </summary>
public FontWeight FontWeight
{
get => GetValue(FontWeightProperty);
set => SetValue(FontWeightProperty, value);
}
private Geometry TextGeometry => _textGeometry ?? CreateTextGeometry();
public override sealed void Render(DrawingContext context)
{
var geometry = TextGeometry;
if (geometry != null)
{
if (_strokePen != null)
{
context.DrawGeometry(null, _strokePen, geometry);
}
if (Foreground != null)
{
context.DrawGeometry(Foreground, null, geometry);
}
}
}
/// <summary>
/// Marks a property as affecting the shape's geometry.
/// </summary>
/// <param name="properties">The properties.</param>
/// <remarks>
/// After a call to this method in a control's static constructor, any change to the
/// property will cause <see cref="InvalidateGeometry"/> to be called on the element.
/// </remarks>
protected static void AffectsGeometry<TOT>(params AvaloniaProperty[] properties)
where TOT : OutlinedTextBlock
{
foreach (var property in properties)
{
property.Changed.Subscribe(e =>
{
if (e.Sender is TOT textBlock)
{
AffectsGeometryInvalidate(textBlock, e);
}
});
}
}
/// <summary>
/// Creates the shape's defining geometry.
/// </summary>
/// <returns>Defining <see cref="Geometry"/> of the shape.</returns>
protected Geometry CreateTextGeometry()
{
if (Text == null)
{
return null;
}
var formattedText = new FormattedText(Text, Thread.CurrentThread.CurrentUICulture, FlowDirection.LeftToRight,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch.Normal), FontSize, Brushes.Black);
return formattedText.BuildGeometry(new Point(0, 0));
}
/// <summary>
/// Invalidates the geometry of this shape.
/// </summary>
protected void InvalidateGeometry()
{
_textGeometry = null;
InvalidateMeasure();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == StrokeProperty || change.Property == StrokeThicknessProperty)
{
InvalidateMeasure();
if (StrokeThickness == 0.0 || Stroke == null)
{
_strokePen = null;
}
else if (_strokePen?.Thickness != StrokeThickness || _strokePen?.Brush != Stroke)
{
_strokePen = new Pen(Stroke, StrokeThickness);
}
}
else if (change.Property == ForegroundProperty)
{
InvalidateVisual();
}
}
protected override Size MeasureOverride(Size availableSize)
=> TextGeometry?.Bounds.BottomRight is Point rb ? new Size(rb.X, rb.Y) : default;
private static void AffectsGeometryInvalidate(OutlinedTextBlock control, AvaloniaPropertyChangedEventArgs e)
{
// If the geometry is invalidated when Bounds changes, only invalidate when the Size
// portion changes.
if (e.Property == BoundsProperty)
{
var oldBounds = (Rect)e.OldValue!;
var newBounds = (Rect)e.NewValue!;
if (oldBounds.Size == newBounds.Size)
{
return;
}
}
control.InvalidateGeometry();
}
}
우와 감사합니다.
저도 구현해보는 중이였는데, 저는 마침 안드로이드 스튜디오에서 java로 구현한 샘플이 있더라고요. 그거 작업 중이였어요 ㅎㅎ
조금 전에 위의 답변에서 레이아웃 관련 코드를 조금 수정했습니다. 참고하세요.