2017-08-22 16 views
0

J'ai créé un bouton triangulaire dans android. Ça marche bien. Je veux juste changer la couleur de remplissage/couleur de trait quand le bouton est survolé/pressé (comme dans le bouton par défaut).bouton Triangle android cliquez/hovered

Je tente d'invalider l'affichage lorsque la propriété survolée a changé et que la couleur de la méthode OnDraw a été modifiée sans succès.

Ceci est mon code:

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); 
    } 
} 

EDIT: code final

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); 
     } 
    } 
} 

Merci pour l'aide

+0

Je ne comprends pas , est-ce un projet Xamarin.Android? Pourquoi voulez-vous créer une sous-classe de 'Button'? Habituellement, nous personnalisons un 'Button' directement en XML. –

+0

Oui, j'utilise Xamarin.Android. Je peux personnaliser le bouton mais je ne peux pas avoir le résultat désiré. Les triangles avec bordure ne sont pas possibles avec le fichier de style ... – Geotinc

Répondre

2

Oui, j'utilise Xamarin.Android. Je peux personnaliser le bouton mais je ne peux pas avoir le résultat désiré. Triangle avec bordure n'est pas possible avec le fichier de style ...

Juste à partir de votre code, je pense que votre "triangle avec bordure" signifie un coup de bouton triangle, alors vous pouvez le créer avec un fichier de ressources drawable, par exemple, créer trois drawable pour la normale, pressée et a plané état:

normal (le code de forme triangulaire vient de 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> 

le code pour l'état « emboutie » et « concentré » sont les mêmes, je juste changé la couleur du coup et solide.

Ensuite, vous pouvez appliquer ce drawable avec le sélecteur comme celui-ci:

<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> 

Enfin, utilisez ce sélecteur comme ceci:

<Button android:layout_height="70dp" 
     android:layout_width="70dp" 
     android:layout_gravity="center_horizontal" 
     android:background="@drawable/triangleSelector" 
     style="?android:attr/borderlessButtonStyle" /> 

Contrôlé le résultat de ma démo (ordre étatique est: normal-> pressed-> a plané):

enter image description here

Mise à jour:

Vous pouvez obtenir les trois côtés caressait comme ceci:

<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> 

Vous pouvez calculer la marge pour le rendre plus beau, il est juste une idée d'ajouter un autre élément à cette couche.

Mise à jour 2:

Outre la méthode que j'ai écrit dans les commentaires, de continuer votre travail pour le faire en classe bouton personnalisé, vous pouvez coder par exemple comme ceci:

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; 
    } 
} 
+0

Merci pour la réponse, je l'ai déjà essayé mais je ne peux pas caresser les trois côtés, et quand vous cliquez en dehors du triangle, le clic est toujours levé. – Geotinc

+0

@Geotinc, pour le problème de trois côtés, j'ai mis à jour ma réponse, vous pouvez avoir un contrôle. Pour le problème que "lorsque vous cliquez en dehors du triangle, le clic est toujours levé", oui, car le bouton par défaut est un rectangle. Travailler sur cela. –

+0

@Geotinc, oh, je vois votre code que pour calculer la zone de réaction pour cliquer, je pense que vous pouvez recalculer et utiliser votre 'TriangularButton' avec sélecteur pour le fond ensemble. –