使用 canvas 清除特定圆圈外的区域
Clear a zone outside a specific circle with canvas
我有一个自定义视图,它通过填充一个圆圈来显示进度,但现在我正在寻找一种方法来擦除我的视图区域在这个白色圆圈之外:
在这里,我的代码:
public class CircleGauge extends View {
private int value = 75;
private Paint backgroundPaint, gaugePaint, textPaint, circlePaint;
... constructors
private void init() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
backgroundPaint = new Paint();
backgroundPaint.setColor(ResourcesCompat.getColor(getResources(), R.color.favorite_position_gauge_background, null));
backgroundPaint.setStyle(Paint.Style.FILL);
backgroundPaint.setAntiAlias(true);
gaugePaint = new Paint();
gaugePaint.setColor(ResourcesCompat.getColor(getResources(), R.color.favorite_position_gauge, null));
gaugePaint.setAntiAlias(true);
circlePaint = new Paint();
circlePaint.setColor(Color.WHITE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, metrics));
circlePaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(Color.WHITE);
textPaint.setFakeBoldText(true);
textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 23, metrics));
textPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);
canvas.drawRect(0, ((float) (100 - value) / 100F) * canvas.getHeight(), canvas.getWidth(), canvas.getHeight(), gaugePaint);
canvas.drawText(getContext().getString(R.string.percent_value, value), getWidth() / 2, getHeight() * .6F, textPaint);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (getHeight() / 2) - circlePaint.getStrokeWidth() / 2, circlePaint);
}
public void setValue(int value) {
this.value = value;
invalidate();
}
}
我很确定我可以用 PorterDuff 做到这一点,但我不知道怎么做。
Android drawable resources 已经为您提供了实现此功能所需的一切,而无需求助于子类化 View
和覆盖 onDraw
.
首先让我们从可绘制资源开始。我们想要一个带有一种颜色的圆圈覆盖另一种颜色的圆圈。为此,我们使用在 XML 和 <layer-list>
中指定的 LayerDrawable
。
每个 Drawable
都有一个 级别 值。您可以通过在 Drawable
上调用 setLevel
来调整级别 (0-10000)。我们想使用级别来控制打火机圆圈的外观。为此,我们将使用在 XML 和 <clip>
.
中定义的 ClipDrawable
对于圈子本身,我们可以使用 ShapeDrawable
s (<shape>
)。这是我们将它们放在一起时的样子:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/field" android:gravity="fill">
<shape android:shape="oval">
<stroke android:width="2dp" android:color="@android:color/white"/>
<solid android:color="#FF78606D"/>
</shape>
</item>
<item android:id="@+id/progress" android:gravity="fill">
<clip android:gravity="bottom" android:clipOrientation="vertical">
<shape android:shape="oval" >
<stroke android:width="2dp" android:color="@android:color/white"/>
<solid android:color="#FFAB9BA6"/>
</shape>
</clip>
</item>
</layer-list>
现在我们可以只使用 `TextView 并将这个可绘制对象作为背景。
<TextView
android:id="@+id/text"
android:layout_height="64dp"
android:layout_width="64dp"
android:layout_gravity="center"
android:background="@drawable/circle_progress"
android:gravity="center"
android:textAppearance="?android:textAppearanceLarge"
android:text="0%"/>
根据您的颜色,我使用了 Theme.AppCompat
深色主题。
这是一个快速而粗略的演示,展示了它们如何协同工作:
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text);
setLevel(0);
new AsyncTask<Void, Integer, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
for (int i = 0; i <= 100; i++) {
publishProgress(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
// just leave
}
return null;
};
@Override
protected void onProgressUpdate(Integer... values) {
setLevel(values[0]);
}
}.execute();
}
private void setLevel(int level) {
mTextView.setText(Integer.toString(level) + "%");
LayerDrawable layerDrawable = (LayerDrawable) mTextView.getBackground();
Drawable progress = layerDrawable.findDrawableByLayerId(R.id.progress);
progress.setLevel(level * 100); // drawable levels go 0-10000
}
}
我有一个自定义视图,它通过填充一个圆圈来显示进度,但现在我正在寻找一种方法来擦除我的视图区域在这个白色圆圈之外:
在这里,我的代码:
public class CircleGauge extends View {
private int value = 75;
private Paint backgroundPaint, gaugePaint, textPaint, circlePaint;
... constructors
private void init() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
backgroundPaint = new Paint();
backgroundPaint.setColor(ResourcesCompat.getColor(getResources(), R.color.favorite_position_gauge_background, null));
backgroundPaint.setStyle(Paint.Style.FILL);
backgroundPaint.setAntiAlias(true);
gaugePaint = new Paint();
gaugePaint.setColor(ResourcesCompat.getColor(getResources(), R.color.favorite_position_gauge, null));
gaugePaint.setAntiAlias(true);
circlePaint = new Paint();
circlePaint.setColor(Color.WHITE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, metrics));
circlePaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(Color.WHITE);
textPaint.setFakeBoldText(true);
textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 23, metrics));
textPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);
canvas.drawRect(0, ((float) (100 - value) / 100F) * canvas.getHeight(), canvas.getWidth(), canvas.getHeight(), gaugePaint);
canvas.drawText(getContext().getString(R.string.percent_value, value), getWidth() / 2, getHeight() * .6F, textPaint);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (getHeight() / 2) - circlePaint.getStrokeWidth() / 2, circlePaint);
}
public void setValue(int value) {
this.value = value;
invalidate();
}
}
我很确定我可以用 PorterDuff 做到这一点,但我不知道怎么做。
Android drawable resources 已经为您提供了实现此功能所需的一切,而无需求助于子类化 View
和覆盖 onDraw
.
首先让我们从可绘制资源开始。我们想要一个带有一种颜色的圆圈覆盖另一种颜色的圆圈。为此,我们使用在 XML 和 <layer-list>
中指定的 LayerDrawable
。
每个 Drawable
都有一个 级别 值。您可以通过在 Drawable
上调用 setLevel
来调整级别 (0-10000)。我们想使用级别来控制打火机圆圈的外观。为此,我们将使用在 XML 和 <clip>
.
ClipDrawable
对于圈子本身,我们可以使用 ShapeDrawable
s (<shape>
)。这是我们将它们放在一起时的样子:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/field" android:gravity="fill">
<shape android:shape="oval">
<stroke android:width="2dp" android:color="@android:color/white"/>
<solid android:color="#FF78606D"/>
</shape>
</item>
<item android:id="@+id/progress" android:gravity="fill">
<clip android:gravity="bottom" android:clipOrientation="vertical">
<shape android:shape="oval" >
<stroke android:width="2dp" android:color="@android:color/white"/>
<solid android:color="#FFAB9BA6"/>
</shape>
</clip>
</item>
</layer-list>
现在我们可以只使用 `TextView 并将这个可绘制对象作为背景。
<TextView
android:id="@+id/text"
android:layout_height="64dp"
android:layout_width="64dp"
android:layout_gravity="center"
android:background="@drawable/circle_progress"
android:gravity="center"
android:textAppearance="?android:textAppearanceLarge"
android:text="0%"/>
根据您的颜色,我使用了 Theme.AppCompat
深色主题。
这是一个快速而粗略的演示,展示了它们如何协同工作:
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text);
setLevel(0);
new AsyncTask<Void, Integer, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
for (int i = 0; i <= 100; i++) {
publishProgress(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
// just leave
}
return null;
};
@Override
protected void onProgressUpdate(Integer... values) {
setLevel(values[0]);
}
}.execute();
}
private void setLevel(int level) {
mTextView.setText(Integer.toString(level) + "%");
LayerDrawable layerDrawable = (LayerDrawable) mTextView.getBackground();
Drawable progress = layerDrawable.findDrawableByLayerId(R.id.progress);
progress.setLevel(level * 100); // drawable levels go 0-10000
}
}