我在android中创建了一个三角形按钮。它运作良好。 我只想在悬停/按下按钮时更改填充颜色/笔触颜色(如默认按钮)。
我尝试在悬停属性更改时使视图无效,并在OnDraw方法中更改颜色但没有成功。
这是我的代码:
public enum TriangularButtonDirection
{
Up = 0,
Down = 1
}
class TriangularButton : Button
{
public TriangularButton(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
this.Initialize(null);
}
public TriangularButton(Context context) : base(context)
{
this.Initialize(null);
}
public TriangularButton(Context context, IAttributeSet attrs) : base(context, attrs)
{
this.Initialize(attrs);
}
public TriangularButton(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
this.Initialize(attrs);
}
public TriangularButton(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
this.Initialize(attrs);
}
private TriangularButtonDirection _direction = TriangularButtonDirection.Down;
private void Initialize(IAttributeSet attributeSet)
{
if (attributeSet != null)
{
TypedArray a = this.Context.ObtainStyledAttributes(attributeSet, Resource.Styleable.TriangularButton);
int direction = a.GetInt(Resource.Styleable.TriangularButton_direction, -1);
if (direction > -1)
this._direction = (TriangularButtonDirection)direction;
a.Recycle();
}
}
public override bool OnTouchEvent(MotionEvent e)
{
float x = e.GetX();
float y = e.GetY();
int width = this.MeasuredWidth;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
bool test = PointInTriangle(new PointF(x, y), point1Draw, point2Draw, point3Draw);
if (test)
base.OnTouchEvent(e);
return (test);
}
public static bool PointInTriangle(PointF p, PointF p0, PointF p1, PointF p2)
{
float s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
float t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
if ((s < 0) != (t < 0))
return false;
float a = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
if (a < 0.0)
{
s = -s;
t = -t;
a = -a;
}
return s > 0 && t > 0 && (s + t) <= a;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
this.SetMeasuredDimension(this.MeasuredWidth, 3 * this.MeasuredWidth / 4);
}
public override void Draw(Canvas canvas)
{
int width = this.MeasuredWidth;
Paint paintFill = new Paint(PaintFlags.AntiAlias);
paintFill.StrokeWidth = 2;
paintFill.Color = this.Hovered ? Color.Red : new Color(242, 180, 54);
paintFill.SetStyle(Android.Graphics.Paint.Style.Fill);
paintFill.AntiAlias = true;
Paint paintStroke = new Paint(PaintFlags.AntiAlias);
paintStroke.StrokeWidth = 2;
paintStroke.Color = Color.White;
paintStroke.SetStyle(Android.Graphics.Paint.Style.Stroke);
paintStroke.AntiAlias = true;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
Path path = new Path();
path.SetFillType(Path.FillType.EvenOdd);
path.MoveTo(point1Draw.X, point1Draw.Y);
path.LineTo(point2Draw.X, point2Draw.Y);
path.LineTo(point3Draw.X, point3Draw.Y);
path.LineTo(point1Draw.X, point1Draw.Y);
path.Close();
canvas.DrawPath(path, paintFill);
canvas.DrawPath(path, paintStroke);
}
}
编辑:最终代码
class TriangularButton : Button
{
public TriangularButton(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
this.Initialize(null);
}
public TriangularButton(Context context) : base(context)
{
this.Initialize(null);
}
public TriangularButton(Context context, IAttributeSet attrs) : base(context, attrs)
{
this.Initialize(attrs);
}
public TriangularButton(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
this.Initialize(attrs);
}
public TriangularButton(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
this.Initialize(attrs);
}
private TriangularButtonDirection _direction = TriangularButtonDirection.Down;
private TriangularButtonState _state = TriangularButtonState.Normal;
private Paint _paintFillNormal;
private Paint _paintStrokeNormal;
private Paint _paintFillPressed;
private Paint _paintStrokePressed;
private void Initialize(IAttributeSet attributeSet)
{
if (attributeSet != null)
{
TypedArray a = this.Context.ObtainStyledAttributes(attributeSet, Resource.Styleable.TriangularButton);
int direction = a.GetInt(Resource.Styleable.TriangularButton_direction, -1);
if (direction > -1)
this._direction = (TriangularButtonDirection)direction;
a.Recycle();
}
this._paintFillNormal = new Paint(PaintFlags.AntiAlias);
this._paintFillNormal.StrokeWidth = 2;
this._paintFillNormal.Color = new Color(242, 180, 54);
this._paintFillNormal.SetStyle(Android.Graphics.Paint.Style.Fill);
this._paintFillNormal.AntiAlias = true;
this._paintFillPressed = new Paint(PaintFlags.AntiAlias);
this._paintFillPressed.StrokeWidth = 2;
this._paintFillPressed.Color = new Color(255, 255, 255, 51);
this._paintFillPressed.SetStyle(Android.Graphics.Paint.Style.Fill);
this._paintFillPressed.AntiAlias = true;
this._paintStrokeNormal = new Paint(PaintFlags.AntiAlias);
this._paintStrokeNormal.StrokeWidth = 2;
this._paintStrokeNormal.Color = Color.White;
this._paintStrokeNormal.SetStyle(Android.Graphics.Paint.Style.Stroke);
this._paintStrokeNormal.AntiAlias = true;
this._paintStrokePressed = new Paint(PaintFlags.AntiAlias);
this._paintStrokePressed.StrokeWidth = 2;
this._paintStrokePressed.Color = new Color(51, 51, 51, 51);
this._paintStrokePressed.SetStyle(Android.Graphics.Paint.Style.Stroke);
this._paintStrokePressed.AntiAlias = true;
}
private bool _isEnabled;
public override bool Enabled
{
get => this._isEnabled;
set
{
if (this._isEnabled != value)
{
this._isEnabled = value;
this.Invalidate();
}
}
}
public override bool OnTouchEvent(MotionEvent e)
{
if (!this._isEnabled)
return (false);
float x = e.GetX();
float y = e.GetY();
int width = this.MeasuredWidth;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
bool test = PointInTriangle(new PointF(x, y), point1Draw, point2Draw, point3Draw);
if (test)
{
base.OnTouchEvent(e);
}
switch (e.Action)
{
case MotionEventActions.Down:
this._state = TriangularButtonState.Pressed;
test = true;
this.Invalidate();
break;
case MotionEventActions.HoverEnter:
this._state = TriangularButtonState.Hovered;
this.Invalidate();
break;
case MotionEventActions.Up:
this._state = TriangularButtonState.Normal;
this.Invalidate();
break;
case MotionEventActions.HoverExit:
this._state = TriangularButtonState.Normal;
this.Invalidate();
break;
}
return (test);
}
public static bool PointInTriangle(PointF p, PointF p0, PointF p1, PointF p2)
{
float s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
float t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
if ((s < 0) != (t < 0))
return false;
float a = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
if (a < 0.0)
{
s = -s;
t = -t;
a = -a;
}
return s > 0 && t > 0 && (s + t) <= a;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
this.SetMeasuredDimension(this.MeasuredWidth, 3 * this.MeasuredWidth / 4);
}
public override void Draw(Canvas canvas)
{
int width = this.MeasuredWidth;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
Path path = new Path();
path.SetFillType(Path.FillType.EvenOdd);
path.MoveTo(point1Draw.X, point1Draw.Y);
path.LineTo(point2Draw.X, point2Draw.Y);
path.LineTo(point3Draw.X, point3Draw.Y);
path.LineTo(point1Draw.X, point1Draw.Y);
path.Close();
if (!this.Enabled)
{
canvas.DrawPath(path, this._paintStrokeNormal);
}
else
{
canvas.DrawPath(path, this._state == TriangularButtonState.Normal ? this._paintFillNormal : this._paintFillPressed);
canvas.DrawPath(path, this._state == TriangularButtonState.Normal ? this._paintStrokeNormal : this._paintStrokePressed);
}
}
}
感谢您的帮助
答案 0 :(得分:2)
是的,我使用的是Xamarin.Android。我可以自定义按钮,但我无法获得所需的结果。样式文件无法使用带边框的三角形...
仅从你的代码中,我认为你的&#34;带边框的三角形&#34;表示三角形按钮笔划,然后您可以使用可绘制资源文件创建它,例如,为正常,按下和悬停状态创建三个drawable:
正常(三角形的代码来自this blog):
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="-40%"
android:pivotY="87%">
<shape
android:shape="rectangle">
<stroke android:color="#800000" android:width="5dp" />
<solid
android:color="#ee9ca8" />
</shape>
</rotate>
</item>
</layer-list>
&#34;代码#34;和#34;专注&#34;状态是一样的,我只是改变了笔画的颜色和实体。
然后你可以用这样的选择器应用这个drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/triangleClick" />
<!-- pressed -->
<item android:state_focused="true"
android:drawable="@drawable/triangleHover" />
<!-- focused -->
<item android:drawable="@drawable/triangleNormal" />
<!-- default -->
</selector>
最后像这样使用这个选择器:
<Button android:layout_height="70dp"
android:layout_width="70dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/triangleSelector"
style="?android:attr/borderlessButtonStyle" />
检查我的演示结果(状态顺序为:normal-&gt; pressed-&gt; hovered):
更新
你可以让三方这样抚摸:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="-40%"
android:pivotY="87%">
<shape
android:shape="rectangle">
<stroke android:color="#800000" android:width="5dp" />
<solid
android:color="#ee9ca8" />
</shape>
</rotate>
</item>
<item android:top="65dp" android:right="5dp" android:left="5dp">
<shape android:shape="rectangle">
<solid android:color="#800000" />
</shape>
</item>
</layer-list>
您可以计算边距以使其更美观,只是想在此图层中添加其他项目。
更新2:
除了我在评论中写的方法,为了继续你在自定义按钮类中这样做的工作,你可以像这样编码:
public override bool OnTouchEvent(MotionEvent e)
{
float x = e.GetX();
float y = e.GetY();
int width = this.MeasuredWidth;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
bool test = PointInTriangle(new PointF(x, y), point1Draw, point2Draw, point3Draw);
if (test)
base.OnTouchEvent(e);
switch (e.Action)
{
case MotionEventActions.Down:
mstate = state.pressed;
this.Invalidate();
break;
case MotionEventActions.HoverEnter:
mstate = state.hovered;
this.Invalidate();
break;
case MotionEventActions.Up:
mstate = state.normal;
this.Invalidate();
break;
case MotionEventActions.HoverExit:
mstate = state.normal;
this.Invalidate();
break;
}
return (test);
}
private state mstate = state.normal;
private enum state
{
normal,
pressed,
hovered
}
public static bool PointInTriangle(PointF p, PointF p0, PointF p1, PointF p2)
{
float s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
float t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
if ((s < 0) != (t < 0))
return false;
float a = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
if (a < 0.0)
{
s = -s;
t = -t;
a = -a;
}
return s > 0 && t > 0 && (s + t) <= a;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
this.SetMeasuredDimension(this.MeasuredWidth, 3 * this.MeasuredWidth / 4);
}
public override void Draw(Canvas canvas)
{
int width = this.MeasuredWidth;
switch (mstate)
{
case state.normal:
Paint paintFill = new Paint(PaintFlags.AntiAlias);
paintFill.StrokeWidth = 2;
paintFill.Color = this.Hovered ? Color.Red : new Color(242, 180, 54);
paintFill.SetStyle(Android.Graphics.Paint.Style.Fill);
paintFill.AntiAlias = true;
Paint paintStroke = new Paint(PaintFlags.AntiAlias);
paintStroke.StrokeWidth = 2;
paintStroke.Color = Color.White;
paintStroke.SetStyle(Android.Graphics.Paint.Style.Stroke);
paintStroke.AntiAlias = true;
PointF point1Draw;
PointF point2Draw;
PointF point3Draw;
if (this._direction == TriangularButtonDirection.Up)
{
point1Draw = new PointF(0, 3f * width / 4);
point2Draw = new PointF(width, 3f * width / 4);
point3Draw = new PointF(width / 2f, 0);
}
else
{
point1Draw = new PointF(0, 0);
point2Draw = new PointF(width, 0);
point3Draw = new PointF(width / 2f, 3f * width / 4);
}
Path path = new Path();
path.SetFillType(Path.FillType.EvenOdd);
path.MoveTo(point1Draw.X, point1Draw.Y);
path.LineTo(point2Draw.X, point2Draw.Y);
path.LineTo(point3Draw.X, point3Draw.Y);
path.LineTo(point1Draw.X, point1Draw.Y);
path.Close();
canvas.DrawPath(path, paintFill);
canvas.DrawPath(path, paintStroke);
break;
case state.hovered:
//TODO:
break;
case state.pressed:
//TODO:
break;
}
}