web-dev-qa-db-ja.com

AndroidのXMLで「長方形の内側に透明な円」の形を作成するにはどうすればよいですか?

アプリで次のデザインを作成しようとしています。

デザインモックアップ
(https://photos.google.com/share/AF1QipPhCGTgf9zi7MetWJYm0NQ14c3wqzjqnEwxsajCFHEjqzt5R29qYvIjZ2C71q7EnQ?key=WUZKSXA1WVVwSlI2LVdTQy1IRjdUdzVuQlpCY0Rn)

メインUIの上にオーバーレイします。 XMLで作成された半透明の形状として背景を持つメインUIの上にレイアウトを使用してこれを作成しようとしています。しかし、複数の投稿を読んだ後でも、私はそれを理解することができません。

次の方法を試しましたが、うまくいきませんでした。 200dpのストロークでリングシェイプを作成し、それをイメージビューのソースとして設定してから、scaletypeをcenterCropに設定しましたが、シェイプはビットマップのように拡大縮小されません。

形状XML:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:innerRadius="0dp"
    Android:shape="ring"
    Android:thicknessRatio="2"
    Android:useLevel="false" >

    <solid Android:color="@Android:color/transparent" />

    <stroke
        Android:width="200dp"
        Android:color="#80000000" />
</shape>

オーバーレイレイアウト:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <ImageView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:src="@drawable/onboarding_background"
        Android:scaleType="centerCrop"/>

</RelativeLayout>

これを行う方法やコードについてのポインタは本当に役に立ちます。

13
abhiank

私は最近似たようなもので遊んでいて、あなたのためにそれを適応させました。すべての魔法はonDrawで起こっています:

_public class FocusView extends View {
  private Paint mTransparentPaint;
  private Paint mSemiBlackPaint;
  private Path mPath = new Path();

  public FocusView(Context context) {
    super(context);
    initPaints();
  }

  public FocusView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initPaints();
  }

  public FocusView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initPaints();
  }

  private void initPaints() {
    mTransparentPaint = new Paint();
    mTransparentPaint.setColor(Color.TRANSPARENT);
    mTransparentPaint.setStrokeWidth(10);

    mSemiBlackPaint = new Paint();
    mSemiBlackPaint.setColor(Color.TRANSPARENT);
    mSemiBlackPaint.setStrokeWidth(10);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPath.reset();

    mPath.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 550, Path.Direction.CW);
    mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);

    canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, 550, mTransparentPaint);

    canvas.drawPath(mPath, mSemiBlackPaint);
    canvas.clipPath(mPath);
    canvas.drawColor(Color.parseColor("#A6000000"));
  }
 }
_

ここでの秘訣は、パス(透明な円)を作成して、パスの描画方法を「パスの内側」ではなく「パスの外側」に設定できるようにすることです。最後に、キャンバスをそのパスにクリップして、黒い色を塗りつぶすだけです。

あなたのために、あなたはただ_Color.BLACK_をあなたの色に変えるだけでなく、希望の半径を変える必要があるでしょう。

編集:ああ、それをプログラムで追加するだけです:FocusView view = new FocusView(context)your_layout.addView(view)

またはXMLで:

_<package_path_to_.FocusView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" />
_

EDIT2:アプリのオンボーディングにこれが必要だと思ったところです。 https://github.com/iammert/MaterialIntroView を見てみることを検討してください。

13
NSimon

NSimonのapilvl16でコードが機能しないという問題が発生しました。コードを修正し、API16以降をサポートするようになりました。

public class FocusView extends View {
    private Paint mPaint;
    private Paint mStrokePaint;
    private Path mPath = new Path();

    public FocusView(Context context) {
        super(context);
        initPaints();
    }

    public FocusView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaints();
    }

    public FocusView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();
    }

    private void initPaints() {
        mPaint = new Paint();
        mPaint.setColor(Color.parseColor("#A6000000"));

        mStrokePaint = new Paint();
        mStrokePaint.setColor(Color.YELLOW);
        mStrokePaint.setStrokeWidth(2);
        mStrokePaint.setStyle(Paint.Style.STROKE);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.reset();

        float radius = 0;
        float strokeWidth = 0;
        if (canvas.getWidth() < canvas.getHeight()) {
            radius = canvas.getWidth() / 2 - 10;
            strokeWidth = (canvas.getHeight() - canvas.getWidth())/2;
        } else {
            radius = canvas.getHeight() / 2 - 10;
            strokeWidth = (canvas.getWidth() - canvas.getHeight())/2;
        }

        mPaint.setStrokeWidth(strokeWidth);

        mPath.addCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, radius, Path.Direction.CW);
        mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);

        canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, radius, mStrokePaint);

        canvas.drawPath(mPath, mPaint);
    }
}

PorterDuffXferMode とそのためのカスタムビューを使用できます。

この写真で提供されているさまざまなモードの良い例(A Out Bを参照): AlphaCompositing

アイデアは、不透明な黒い長方形とその上に円を描くカスタムビューを作成することです。 PorterDuffXferMode.SRC_OUTを適用すると、長方形から円が「消去」されるため、希望どおりの結果が得られます。

カスタムビューでは、dispatchDraw(Canvas canvas)メソッドをオーバーライドし、結果のビットマップをフレームに描画する必要があります。

次に、MapViewとカスタムビューをFrameLayoutに配置して、結果を楽しむことができます。

1
noktigula