2015-12-04 35 views
15

我想製作一個自定義按鈕,就像這個程序可能使用徑向漸變。Android - 自定義按鈕,帶有形狀繪圖和漸變編程

我創建了子視圖,然後繪製三個形狀的drawable,然後繪製文本。文字似乎偏離中心,所以我試圖爲文字畫出一個邊界矩形,但沒有運氣。並計劃添加一個點擊偵聽器來獲取類似按鈕的行爲。

也許我應該繼承按鈕,但是在哪裏繪製我的drawable,這樣它們就不會被繪製的按鈕的文本弄亂。

任何指針將不勝感激。

謝謝

編輯2:請參閱下面的第二次嘗試。

編輯3:賞金的原因是找出爲什麼子類drawable不起作用。漸變並不重要。

edit4:在DrawableView :: OnDraw()中的getTextBounds()之前發現了drawRect。

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

此版本試圖繪製子類並使用按鈕。但按鈕的繪圖似乎干擾繪製我的可繪製形狀。它看起來像界限被忽略。

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

所有你需要的是什麼,是創建一個自定義的'Drawable'類 – pskink

+0

您可以繪製使用梯度文件,並設置爲背景 – curiousMind

+0

@pskink,所以我繼承和繪製給它設置背景的buttton? –

回答

4

1)使用對齊畫在中心文本在DrawableView(應幫助文本似乎偏離中心):

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)要回答你的問題賞金的理由是弄清楚爲什麼繼承drawable不會w掃

我想這是因爲你創建MyDrawableDrawableView,並且不將其添加到這意味着你沒有衡量和佈局沒有任何容器。所以,它可能是零高度和寬度。

3)我建議你使用Button而不是自定義視圖和drawables。您可以從按鍵中的onDraw方法端延伸,做更多的圖紙,像這樣:

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

原始錯誤的答案

爲賞金的原因是爲了弄清楚爲什麼子類化繪製不不行

嘗試檢查,如果你需要調用:

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

super.draw()在MyDrawable中是抽象的。 –

+0

DrawableView中沒有super.OnDraw()。 –

+0

@RayTayek更新了我的答案,修復了無定製可繪製文本的居中定位的可能答案,以解釋爲什麼可繪製子類不適用於您的案例和小建議。 – GregoryK

2

使用這個代碼,以使漸變按鈕

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

我想以編程方式做到這一點。 –

2

這不是爲了創造可繪製依賴於一個視圖是一個好主意。按照Eugen Pechanec的建議,使MyDrawable和DrawableView成爲靜態。

您只在MyDrawable中使用ShapeDrawable,因此您可以將它從DrawableView中移出。

它可以是這樣的:

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

你可以考慮不使用ShapeDrawable和自己繪製形狀:

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

順便說一句,這是很好的使用StateListDrawable爲一個按鈕。
所以,你可以使用MyDrawable這樣的:

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

現在這兩個類都是靜態的。如果我真的想要一個奇特的按鈕,我會把這個垃圾從頭開始。和子類按鈕,也許使用可繪製的狀態列表。謝謝 –

+0

如果你只是想改變它的設計,就不需要子類化Button。 Button擴展了TextView,因此您還可以輕鬆控制文本外觀。但是如果你想改變字體,你需要繼承它。 –

+0

我希望改變字體的大小,也許字體本身。 –