2015-12-04 8 views
15

Ich möchte eine benutzerdefinierte Schaltfläche wie dieses Programm mit vielleicht einen radialen Farbverlauf machen.Android - benutzerdefinierte Schaltfläche mit Form zeichnest und einem Gradienten programmgesteuert

Ich subclassed Ansicht, und zeichne drei Formen Zeichen und dann den Text zu zeichnen. der Text scheint nicht zentriert zu sein, also habe ich versucht, ein begrenzendes Rechteck für den Text zu zeichnen, aber kein Glück. und plante, einen Klick-Listener hinzuzufügen, um Button-ähnliches Verhalten zu erhalten.

vielleicht sollte ich Unterklasse Button, aber wo zeichne meine Zeichensätze, damit sie nicht durch den Text der Schaltfläche gezeichnet werden verwirrt werden.

Alle Hinweise werden geschätzt.

danke

Edit2: siehe zweiten Versuch unten.

Edit3: Der Grund für die Bounty ist herauszufinden, warum Subclassing Drawable nicht funktioniert. der Gradient ist nicht so wichtig.

edit4: entdeckt drawRect vor getTextBounds() in DrawableView :: OnDraw().

package acme.drawables; 
import android.content.*; 
import android.content.pm.*; 
import android.graphics.*; 
import android.graphics.drawable.*; 
import android.graphics.drawable.shapes.*; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.util.*; 
import android.view.Menu; 
import android.view.View; 
import android.widget.*; 
import static java.lang.Math.*; 
public class MainActivity extends AppCompatActivity { 
    DrawableView drawableView; 
    LinearLayout row(boolean isRow1) { 
     LinearLayout layout=new LinearLayout(this); 
     layout.setOrientation(LinearLayout.HORIZONTAL); 
     LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(w,d); 
     int m=(int)round(w*margin); 
     layoutParams.setMargins(m,m,m,m); 
     for(int i=0;i<n;i++) 
      layout.addView(drawableView=new DrawableView(this,i,isRow1),layoutParams); 
     return layout; 
    } 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
     DisplayMetrics metrics=new DisplayMetrics(); 
     getWindowManager().getDefaultDisplay().getMetrics(metrics); 
     w=d=(int)round(metrics.densityDpi); 
     LinearLayout row1=row(true); 
     LinearLayout row2=row(false); 
     LinearLayout layout=new LinearLayout(this); 
     layout.setOrientation(LinearLayout.VERTICAL); 
     layout.addView(row1); 
     layout.addView(row2); 
     LinearLayout l=new LinearLayout(this); 
     setContentView(layout); 
    } 
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     return true; 
    } 
    public class DrawableView extends View { 
     public DrawableView(Context context,int column,boolean isRow1) { 
      super(context); 
      setBackgroundColor(Color.TRANSPARENT); 
      this.column=column; 
      text=""+(char)('0'+column); 
      int r=(int)round(w*radius); 
      d0=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      d0.getPaint().setColor(0xff000000); 
      d0.setBounds(0,0,w,d); 
      d1=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      d1.getPaint().setColor(on[column]); 
      d1.setBounds(edge,edge,w-edge,d-edge); 
      d2=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      int b=(int)round(w*border); 
      d2.setBounds(b/2,b/2,w-b/2,d-b/2); 
      d2.getPaint().setColor(isRow1?on[column]:off[column]); 
     } 
     protected void onDraw(Canvas canvas) { 
      d0.draw(canvas); 
      d1.draw(canvas); 
      d2.draw(canvas); 
      Paint paint = new Paint(); 
      //paint.setColor(Color.WHITE); 
      paint.setStyle(Paint.Style.FILL); 
      //canvas.drawPaint(paint); 
      paint.setColor(Color.CYAN); 
      paint.setTextSize(w*95/100); 
      Rect r=new Rect(); 
      paint.getTextBounds(text,0,1,r); // were switched 
      canvas.drawRect(r,paint); // were switched 
      int x=(w-r.width())/2,y=(d-r.height())/2; 
      paint.setColor(Color.BLACK); 
      canvas.drawText(text,x,d-y,paint); 
     } 
     final int column; 
     final String text; 
     ShapeDrawable d0, d1, d2; 
    } 
    final int n=5, edge=1; 
    double margin=.10, radius=.05, border=.15; 
    int w, d; 
    final int[] on=new int[]{0xffff0000,0xffffff00,0xff00ff00,0xff0000ff,0xffff8000}; 
    final int[] off=new int[]{0xff800000,0xff808000,0xff008000,0xff000080,0xff804000}; 
} 

Diese Version versucht, Zeichenklassen unterziehen und verwenden Sie eine Schaltfläche. aber die Zeichnung des Knopfes scheint meine zeichnenden Formen zu stören. Es sieht so aus, als würden die Grenzen ignoriert.

package acme.drawables; 
import android.content.*; 
import android.content.pm.*; 
import android.graphics.*; 
import android.graphics.drawable.*; 
import android.graphics.drawable.shapes.*; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.util.*; 
import android.view.Menu; 
import android.view.View; 
import android.widget.*; 

import static java.lang.Math.*; 
public class MainActivity extends AppCompatActivity { 
    LinearLayout row(boolean isRow1) { 
     LinearLayout layout=new LinearLayout(this); 
     layout.setOrientation(LinearLayout.HORIZONTAL); 
     LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(w,d); 
     int m=(int)round(w*margin); 
     layoutParams.setMargins(m,m,m,m); 
     if(true) 
      for(int i=0;i<n;i++) { // subclass drawable 
       Button b=new Button(this); 
       b.setText(""+(char)('0'+i)); 
       b.setBackground(new MyDrawable(i,i/n%2==0)); 
       layout.addView(b,layoutParams); 
      } 
     else 
      for(int i=0;i<n;i++) // use drawable view with canvas draw text 
       layout.addView(drawableView=new DrawableView(i,isRow1),layoutParams); 
     return layout; 
    } 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
     DisplayMetrics metrics=new DisplayMetrics(); 
     getWindowManager().getDefaultDisplay().getMetrics(metrics); 
     w=d=(int)round(metrics.densityDpi); 
     LinearLayout row1=row(true); 
     LinearLayout row2=row(false); 
     LinearLayout layout=new LinearLayout(this); 
     layout.setOrientation(LinearLayout.VERTICAL); 
     layout.addView(row1); 
     layout.addView(row2); 
     LinearLayout l=new LinearLayout(this); 
     setContentView(layout); 
    } 
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     return true; 
    } 
    public class MyDrawable extends Drawable { 
     public MyDrawable(int column,boolean isRow1) { 
      drawableView=new DrawableView(column,isRow1); 
     } 
     public void setAlpha(int alpha) { 
      System.out.println("ignoring set alpha to: "+alpha); 
     } 
     public void setColorFilter(ColorFilter colorFilter) { 
      System.out.println("ignoring set color filter to: "+colorFilter); 
     } 
     public int getOpacity() { 
      return PixelFormat.OPAQUE; 
     } 
     public void draw(Canvas canvas) { 
      System.out.println(this+" is drawing."); 
      drawableView.d0.draw(canvas); 
      System.out.println("d0 bounds: "+drawableView.d0.getBounds()); 
      drawableView.d1.draw(canvas); 
      System.out.println("d1 bounds: "+drawableView.d1.getBounds()); 
      drawableView.d2.draw(canvas); 
      System.out.println("d2 bounds: "+drawableView.d2.getBounds()); 
     } 
     final DrawableView drawableView; // cheat by delegating 
    } 
    public class DrawableView extends View { 
     public DrawableView(int column,boolean isRow1) { 
      super(MainActivity.this); 
      setBackgroundColor(Color.TRANSPARENT); 
      this.column=column; 
      text=""+(char)('0'+column); 
      int r=(int)round(w*radius); 
      d0=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      d0.getPaint().setColor(0xff000000); 
      d0.setBounds(0,0,w,d); 
      d1=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      d1.getPaint().setColor(on[column]); 
      d1.setBounds(edge,edge,w-edge,d-edge); 
      d2=new ShapeDrawable(new RoundRectShape(new float[]{r,r,r,r,r,r,r,r},null,null)); 
      int b=(int)round(w*border); 
      d2.setBounds(b/2,b/2,w-b/2,d-b/2); 
      d2.getPaint().setColor(isRow1?on[column]:off[column]); 
     } 
     protected void onDraw(Canvas canvas) { 
      d0.draw(canvas); 
      d1.draw(canvas); 
      d2.draw(canvas); 
      Paint paint=new Paint(); 
      //paint.setColor(Color.WHITE); 
      paint.setStyle(Paint.Style.FILL); 
      //canvas.drawPaint(paint); 
      paint.setColor(Color.CYAN); 
      paint.setTextSize(w*95/100); 
      Rect r=new Rect(); 
      canvas.drawRect(r,paint); 
      paint.getTextBounds(text,0,1,r); 
      int x=(w-r.width())/2, y=(d-r.height())/2; 
      paint.setColor(Color.BLACK); 
      canvas.drawText(text,x,d-y,paint); 
     } 
     final int column; 
     final String text; 
     ShapeDrawable d0, d1, d2; 
    } 
    DrawableView drawableView; 
    final int n=5, edge=1; 
    double margin=.10, radius=.05, border=.15; 
    int w, d; 
    final int[] on=new int[]{0xffff0000,0xffffff00,0xff00ff00,0xff0000ff,0xffff8000}; 
    final int[] off=new int[]{0xff800000,0xff808000,0xff008000,0xff000080,0xff804000}; 
} 
+0

alles, was Sie brauchen, ist eine benutzerdefinierte 'Drawable' Klasse erstellen – pskink

+0

Sie Gradienten-Datei in ziehbar und setzen als Hintergrund – curiousMind

+0

@pskink verwenden können, so eine Unterklasse i ziehbar und Gib es einem Buttton mit gesetztem Hintergrund? –

Antwort

4

1), um die Ausrichtung Verwenden Sie Text in der Mitte zu ziehen, in Ihrem DrawableView (mit helfen sollte der Text weg scheint Zentrum):

paint.setTextAlign(Align.CENTER); // <- should help you with centering 
paint.getTextBounds(text, 0, 1, r); 
int x = w/2, y = (d - r.height())/2; // <- was updated too 

2) Um Ihre Frage zu beantworten, ist der Grund für das Kopfgeld herauszufinden, warum Subclassing Drawable nicht w ork:

Ich nehme an, es ist, weil Sie DrawableView in MyDrawable erstellen und es nicht zu jedem Container hinzufügen, die Sie messen nicht bedeutet, und es Layout. Also, es ist wahrscheinlich Null Höhe und Breite.

3) Ich würde vorschlagen, dass Sie Button anstelle von benutzerdefinierten Ansichten und Zeichen verwenden.Sie erstrecken sich von Knopf und machen zusätzliche Zeichnungen am Ende der OnDraw-Methode, so etwas wie dieses:

public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 
    // your custom drawing over button 
} 

Ursprüngliche falsche Antwort

der Grund für die Prämie, um herauszufinden, ist, warum Subklassifizieren ziehbar tut nicht funktionieren

Versuchen zu überprüfen, ob Sie anrufen müssen:

  • super.onDraw(canvas) in DrawableView.onDraw
  • super.draw(canvas) in MyDrawable.draw
+0

super.draw() ist in MyDrawable abstrakt. –

+0

gibt es keine Super.OnDraw() in DrawableView. –

+0

@RayTayek aktualisiert meine Antwort mit der Möglichkeit, Text aus der Zentrierung zu korrigieren, ohne benutzerdefinierte Zeichen, mögliche Antwort auf, warum Zeichnungsunterklasse in Ihrem Fall nicht funktioniert und einen kleinen Vorschlag. – GregoryK

2

Verwendung dieser Code Gradientknopf

Button your_button= findViewById(R.id.button); 

    GradientDrawable gd = new GradientDrawable(
      GradientDrawable.Orientation.TOP_BOTTOM, 
      new int[] {0xFF616261,0xFF131313}); 
    gd.setCornerRadius(0f); 

    your_button.setBackgroundDrawable(gd); 
+0

Ich möchte dies programmgesteuert tun. –

2

Es ist keine gute Idee zu schaffen Drawable auf einem Blick abhing. Wie Eugen Pechanec vorgeschlagen hat, mache MyDrawable und DrawableView statisch.

Sie verwenden ShapeDrawables nur in MyDrawable, sodass Sie es aus DrawableView verschieben können.

Es kann so etwas wie dieses:

public static class MyDrawable extends Drawable { 
    private ShapeDrawable d0, d1, d2; 
    private int edge; 
    private int border; 

    public MyDrawable(int color1, int color2, int radius, int edge, int border) { 
     this.edge = edge; 
     this.border = border; 

     float[] outerRadii = new float[] { 
       radius, radius, 
       radius, radius, 
       radius, radius, 
       radius, radius 
     }; 

     d0 = new ShapeDrawable(new RoundRectShape(outerRadii, null, null)); 
     d0.getPaint().setColor(0xff000000); 
     d1 = new ShapeDrawable(new RoundRectShape(outerRadii, null, null)); 
     d1.getPaint().setColor(color1); 
     d2 = new ShapeDrawable(new RoundRectShape(outerRadii, null, null)); 
     d2.getPaint().setColor(color2); 
    } 

    public void setAlpha(int alpha) { 
     System.out.println("ignoring set alpha to: " + alpha); 
    } 

    public void setColorFilter(ColorFilter colorFilter) { 
     System.out.println("ignoring set color filter to: " + colorFilter); 
    } 

    public int getOpacity() { 
     return PixelFormat.OPAQUE; 
    } 

    public void draw(Canvas canvas) { 
     System.out.println(this + " is drawing."); 
     d0.draw(canvas); 
     System.out.println("d0 bounds: " + d0.getBounds()); 
     d1.draw(canvas); 
     System.out.println("d1 bounds: " + d1.getBounds()); 
     d2.draw(canvas); 
     System.out.println("d2 bounds: " + d2.getBounds()); 
    } 

    @Override 
    public void setBounds(int left, int top, int right, int bottom) { 
     super.setBounds(left, top, right, bottom); 
     d0.setBounds(left, top, right, bottom); 
     d1.setBounds(left + edge, top + edge, right - edge, bottom - edge); 
     d2.setBounds(left + border/2, top + border/2, 
       right - border/2, bottom - border/2); 
    } 
} 

Sie betrachten können nicht ShapeDrawable verwenden und die Formen selbst zeichnen:

public static class MyDrawable extends Drawable { 
    private int radius; 
    private int edge; 
    private int border; 

    private RectF bounds1 = new RectF(); 
    private RectF bounds2 = new RectF(); 
    private RectF bounds3 = new RectF(); 

    private Paint paint1 = new Paint(); 
    private Paint paint2 = new Paint(); 
    private Paint paint3 = new Paint(); 

    public MyDrawable(int color1, int color2, int radius, int edge, int border) { 
     this.radius = radius; 
     this.edge = edge; 
     this.border = border; 

     float[] outerRadii = new float[] { 
       radius, radius, 
       radius, radius, 
       radius, radius, 
       radius, radius 
     }; 

     paint1.setColor(0xff000000); 
     paint2.setColor(color1); 
     paint3.setColor(color2); 
    } 

    public void setAlpha(int alpha) { 
     System.out.println("ignoring set alpha to: " + alpha); 
    } 

    public void setColorFilter(ColorFilter colorFilter) { 
     System.out.println("ignoring set color filter to: " + colorFilter); 
    } 

    public int getOpacity() { 
     return PixelFormat.OPAQUE; 
    } 

    public void draw(Canvas canvas) { 
     canvas.drawRoundRect(bounds1, radius, radius, paint1); 
     canvas.drawRoundRect(bounds2, radius, radius, paint2); 
     canvas.drawRoundRect(bounds3, radius, radius, paint3); 
    } 

    @Override 
    public void setBounds(int left, int top, int right, int bottom) { 
     super.setBounds(left, top, right, bottom); 

     bounds1.set(left, top, right, bottom); 

     bounds2.set(bounds1); 
     bounds2.inset(edge, edge); 

     bounds3.set(bounds1); 
     bounds3.inset(border/2, border/2); 
    } 
} 

By the way, ist es gut, StateListDrawable zu verwenden für ein Knopf.
So können Sie MyDrawable wie folgt verwenden:

MyDrawable drawable = new MyDrawable(...); 
MyDrawable drawablePressed = new MyDrawable(...); 
MyDrawable drawableFocused = new MyDrawable(...); 

StateListDrawable stateDrawable = new StateListDrawable(); 
stateDrawable.addState(new int[]{android.R.attr.state_pressed}, drawablePressed); 
stateDrawable.addState(new int[]{android.R.attr.state_focused}, drawableFocused); 
stateDrawable.addState(new int[]{}, drawable); 

Button button = (Button) findViewById(R.id.button); 
button.setBackground(stateDrawable); 
+0

beide Klassen sind jetzt statisch. Ich werde das wegwerfen und neu anfangen, wenn ich wirklich einen ausgefallenen Knopf will. und Unterklasse-Schaltfläche und möglicherweise die Statusliste ziehbar. Danke –

+0

Es gibt keine Notwendigkeit, Button zu untergliedern, wenn Sie nur sein Design ändern möchten. Schaltfläche erweitert TextView, so dass Sie auch die Textdarstellung einfach steuern können. Aber Sie müssen es Unterklasse, wenn Sie die Schriftart ändern möchten. –

+0

Ich möchte die Größe der Schriftart und vielleicht die Schriftart selbst ändern. –

Verwandte Themen