web-dev-qa-db-ja.com

キャンバスに放射状のグラデーションで円を描く方法は?

関数を呼び出すと彼の色を変えることができる円ボタンを作成しました。私が欲しいのは、同じ円形ボタンを作成する別のボタンを作成することですが、放射状のグラデーションが中央から始まり、色が選択され、円から出ると透明になります。

グラデーションスタイルをペイントオブジェクトに設定する方法は? に投稿されたものを使用して同様のコードを作成しましたが、機能しません。

私が試したコードは、このポープスに対するものです。

mPaint.setShader(new RadialGradient(0, 0, height/3, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.MIRROR));

次のクラスは、サークルボタン用に作成したクラスです。

public class ColorGradientCircleButton extends View{

private Paint mPaint;
private Paint   mBitmapPaint;
private Bitmap  mBitmap;
private Canvas  mCanvas;
private int width, height;

public ColorGradientCircleButton(Context context) {
    super(context);
    init();
}
public ColorGradientCircleButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}
public ColorGradientCircleButton(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}
private void init() {
    mPaint = new Paint();
    mPaint.setColor(Color.BLACK);
    mPaint.setStrokeWidth(1);
    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    width = w;
    height = h;
    mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    mCanvas = new Canvas(mBitmap);
    mCanvas.drawCircle(w/2, h/2, h/3, mPaint);
}
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
}
public void changeColor(int color){
    mPaint.setColor(color);
    mCanvas.drawCircle(width/2, height/2, height/3, mPaint);
    invalidate();
}
}
16
Gabriel Esteban

これを回答ボックスに移行する必要があります。

OPは基本的にここにそれを持っています-そして実際にOPの改訂された 要点 は素晴らしいです。

質問の最初の試みに関するいくつかの一般的なヒント:

1)protected void onSizeChanged(int w, int h, int oldw, int oldh)内:

  • _width = w;_これが必要なときにgetWidth()を呼び出せない理由はありません。これが推奨される理由は、Viewの内部幅がonMeasureのかなり後に設定されるためです。したがって、onDrawが次に最新のバージョンを必要とする可能性があるため、そこでゲッターを使用します。
  • mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);。ビットマップの作成は、コストがかかり、メモリを大量に消費する操作です。ビットマップをファイルに書き込んだり、BitmapDrawableなどのImageViewに送信したりする場合を除いて、これを行う必要はありません。特に、Androidのgraphicsライブラリを使用してUIに描画されたエフェクトで。
  • mCanvas = new Canvas(mBitmap);の後に新しいキャンバスへの描画操作が続きます。これは決して必要ありません。それでも、多くのコードベースや試行で(動作しない)それを確認しました。私thinkこれは、キャンバスの残りの部分に描画を行わずにカスタムビューでキャンバスを変換できるようにするために、古いスタックオーバーフローポストのせいです。ちなみに、これが必要な場合は、代わりに.restore().save()を使用してください。 _new Canvas_が表示された場合、疑わしい

2)onDraw(...)

  • はい、オブジェクトの作成や重い処理など、onDrawでの作業を回避する必要があります。 しかしonDrawで行う必要があることはonDrawで行う必要があります!
  • ここでは単に呼び出す必要がありますcanvas.drawCircle(float cx, float cy, float radius, Paint paint)docs のような引数を使用します。
  • これは、onDrawにとってそれほど罪深いことではありません。 これを呼び出しすぎるのが心配な場合、ボタン全体が画面全体でアニメーション化されている場合のように、 ハードウェアアクセラレーション を使用する必要があります。 = ビューの最適化 ;という記事で詳しく説明するように、以降のAPIバージョンで使用できます。カスタム描画ビューをたくさん使用している場合は、非常に役立つ読み物です。

3)その厄介な放射状グラデーション。次の問題は、initmethodでペイントを正しく作成したため、オブジェクトの作成が適切に行われなかったことです。しかし、その段階ではビューのgetHeight()が0だったので、当然のことながらIllegalArgumentExceptioned(私は思う)が表示されます。小さなピクセル値を渡そうとしましたが、魔法を知らない限り機能しません。画面サイズについて。

これは、Androidのデザインパターンの中心にある煩わしいビューサイクルほど問題ではありません。ただし、修正は簡単です。onMeasure呼び出しの後にビューの描画プロセスの後の部分を使用して、Paintフィルターを設定するだけです。

しかしこれを正しく行うにはいくつかの問題があります。つまり、予期したポイントの前にonDrawが呼び出されることがあります。その結果、ペイントがnullになり、目的の動作が得られなくなります。

より堅牢な解決策は、onDrawで生意気でいたずらな小さなnullチェックを実行し、一度だけそこにPaintオブジェクトを作成することです。厳密に言えば最適ではありませんが、PaintオブジェクトがAndroidのグラフィックネイティブレイヤーに接続する複雑な方法を考えると、頻繁に呼び出される多くの場所でペイントの構成と構築にまたがるよりも優れています。そして、それはくっきりしたより明確なコードになります。

これは次のようになります(要点を修正):

_        @Override
        protected void onDraw(final Canvas canvas) {
            super.onDraw(canvas);
            if (mPaint == null) {
                mPaint = new Paint();
                mPaint.setColor(Color.BLACK);
                mPaint.setStrokeWidth(1);
                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
                mPaint.setShader(new RadialGradient(getWidth() / 2, getHeight() / 2,
                        getHeight() / 3, Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));
            }
            width = getWidth();
            height = getHeight();
            canvas.drawCircle(width / 2, height / 2, height / 3, mPaint);
        }
_

したがって、いくつかの変更点に注意してください。説明から、引数で2つの色を入れ替える必要があると思います。また、グラデーションの中心をビューの中央に配置することを忘れないでください。_width/2_および_height/2_引数。

頑張ってください!

20
Tom