最初の 2 つの問題にはマスクを使用します。
1.滑らかなグラデーションを作成する
最初のステップは、線形グラデーションで 2 つの四角形を描画することです。次の図に示すように、最初の四角形には青と緑の色が含まれ、2 つ目の四角形には緑と赤が含まれます。両方の長方形が互いに接する線を黒くマークして、実際には 2 つの異なる長方形であることを明確にしました。
これは、次のコード (抜粋) を使用して実現できます。
// Both color gradients
private Shader shader1 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(101, 172, 242), Shader.TileMode.CLAMP);
private Shader shader2 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(255, 31, 101), Shader.TileMode.CLAMP);
private Paint paint = new Paint();
// ...
@Override
protected void onDraw(Canvas canvas) {
float width = 800;
float height = 800;
float radius = width / 3;
// Arc Image
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // See other config types
Bitmap mImage = Bitmap.createBitmap(800, 800, conf); // This creates a mutable bitmap
Canvas imageCanvas = new Canvas(mImage);
// Draw both rectangles
paint.setShader(shader1);
imageCanvas.drawRect(0, 0, 400, 800, paint);
paint.setShader(shader2);
imageCanvas.drawRect(400, 0, 800, 800, paint);
// /Arc Image
// Draw the rectangle image
canvas.save();
canvas.drawBitmap(mImage, 0, 0, null);
canvas.restore();
}
目標は丸みを帯びたキャップを持つ色付きの円弧にすることなので、次に、ユーザーに表示される両方の長方形の領域を定義する必要があります。これは、両方の長方形のほとんどがマスクされて見えなくなることを意味します。代わりに残るのはアーク領域だけです。
結果は次のようになります。
必要な動作を実現するために、長方形内の円弧領域のみを表示するマスクを定義します。setXfermode
このために、 のメソッドを多用しますPaint
。引数として、 のさまざまなインスタンスを使用しますPorterDuffXfermode
。
private Paint maskPaint;
private Paint imagePaint;
// ...
// To be called within all constructors
private void init() {
// I encourage you to research what this does in detail for a better understanding
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
imagePaint = new Paint();
imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}
@Override
protected void onDraw(Canvas canvas) {
// @step1
// Mask
Bitmap mMask = Bitmap.createBitmap(800, 800, conf);
Canvas maskCanvas = new Canvas(mMask);
paint.setColor(Color.WHITE);
paint.setShader(null);
paint.setStrokeWidth(70);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAntiAlias(true);
final RectF oval = new RectF();
center_x = 400;
center_y = 400;
oval.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
maskCanvas.drawArc(oval, 135, 270, false, paint);
// /Mask
canvas.save();
// This is new compared to step 1
canvas.drawBitmap(mMask, 0, 0, maskPaint);
canvas.drawBitmap(mImage, 0, 0, imagePaint); // Notice the imagePaint instead of null
canvas.restore();
}
2. オーバーレイの白い親指を作成します
これで最初の問題は解決します。2 つ目はマスクを使用して実現できますが、今回は別のことを実現したいと考えています。以前は、背景画像 (2 つの長方形) の特定の領域 (円弧) のみを表示したいと考えていました。今回は反対のことを行います。背景イメージ (親指) を定義し、その内部コンテンツをマスクして、ストロークだけが残るようにします。弧の画像に適用された親指は、透明なコンテンツ領域で色付きの弧を覆います。
したがって、最初のステップは親指を描くことです。これには、背景の円弧と同じ半径で異なる角度の円弧を使用するため、はるかに小さな円弧になります。ただし、親指は背景の弧を「取り囲む」必要があるため、そのストローク幅は背景の弧よりも大きくする必要があります。
@Override
protected void onDraw(Canvas canvas) {
// @step1
// @step2
// Thumb Image
mImage = Bitmap.createBitmap(800, 800, conf);
imageCanvas = new Canvas(mImage);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(120);
final RectF oval2 = new RectF();
center_x = 400;
center_y = 400;
oval2.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
imageCanvas.drawArc(oval2, 270, 45, false, paint);
// /Thumb Image
canvas.save();
canvas.drawBitmap(RotateBitmap(mImage, 90f), 0, 0, null);
canvas.restore();
}
public static Bitmap RotateBitmap(Bitmap source, float angle)
{
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}
コードの結果を以下に示します。
親指が背景の円弧に重なったので、親指の内側の部分を削除するマスクを定義して、背景の円弧が再び見えるようにする必要があります。
これを実現するために、基本的には以前と同じパラメータを使用して別の弧を作成しますが、今回は、ストロークの幅を背景の弧に使用した幅と同じにする必要があります。これは、親指の内側で削除する領域をマークするためです。
次のコードを使用すると、結果の画像が画像 4 に表示されます。
@Override
protected void onDraw(Canvas canvas) {
// @step1
// @step2
// Thumb Image
// ...
// /Thumb Image
// Thumb Mask
mMask = Bitmap.createBitmap(800, 800, conf);
maskCanvas = new Canvas(mMask);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(70);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
final RectF oval3 = new RectF();
center_x = 400;
center_y = 400;
oval3.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
maskCanvas.drawBitmap(mImage, 0, 0, null);
maskCanvas.drawArc(oval3, 270, 45, false, paint);
// /Thumb Mask
canvas.save();
canvas.drawBitmap(RotateBitmap(mMask, 90f), 0, 0, null); // Notice mImage changed to mMask
canvas.restore();
}
3. 白い親指をアニメーション化する
質問の最後の部分は、弧の動きをアニメーション化することです。これに対する確実な解決策はありませんが、役立つ方向に導くことができるかもしれません。私は次のことを試します:
ImageView
最初に、円弧グラフ全体の一部であるとして親指を定義します。グラフの選択した値を変更するときは、背景の円弧の中心を中心にサム イメージを回転させます。動きをアニメートしたいので、親指の画像の回転を設定するだけでは不十分です。代わりに、RotateAnimation
次のようなものを使用します。
final RotateAnimation animRotate = new RotateAnimation(0.0f, -90.0f, // You have to replace these values with your calculated angles
RotateAnimation.RELATIVE_TO_SELF, // This may be a tricky part. You probably have to change this to RELATIVE_TO_PARENT
0.5f, // x pivot
RotateAnimation.RELATIVE_TO_SELF,
0.5f); // y pivot
animRotate.setDuration(1500);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);
thumbView.startAnimation(animSet);
これは最終的なものではないと思いますが、必要な解決策を探すのに非常に役立つかもしれません. ピボット値が背景の円弧の中心を参照する必要があることは非常に重要です。これは、親指の画像が回転するポイントであるためです。
API レベル 16 および 22、23 で (完全な) コードをテストしたので、この回答が少なくとも問題の解決方法に関する新しいアイデアを提供してくれることを願っています。
メソッド内での割り当て操作onDraw
は悪い考えであり、避けるべきであることに注意してください。簡単にするために、私はこのアドバイスに従わなかった. また、このコードはマジック ナンバーを多用し、一般的に適切なコーディング基準に従っていないため、単純にコピー アンド ペーストするのではなく、正しい方向へのガイドとして使用する必要があります。