角丸のViewをつくる

たぶん、これが一番楽だと思います。

# 角丸

マテリアルデザインではあまり使うことはありませんが、プレビューの画像を角丸にしたいだとか稀によくあるかと思います。
実装方法もいろいろありますが、今回は割りと汎用性高そうな実装方法を紹介します。

# FrameLayoutこそ至高

`ViewGroup` の実装はめちゃくそつらいですが、とりあえず `FrameLayout`を拡張すればなんとかなります。
`LinearLayout` のように横に並べたかったとしても、とりあえず `FrameLayout`の子に `LinearLayout`置けばいいだけなので、 `LinearLayout`を拡張するなんてことはしません。どう考えても楽じゃない。

# サンプルコード

今回は`View`にある `dispatchDraw(Canvas)` メソッドを利用します。
要するに子Viewのdrawに対して角丸にclipするだけです。

```CornerClipFrameLayout.java
public class RoundClipFrameLayout extends FrameLayout {

    private final Path mPath = new Path();

    private final RectF mRect = new RectF();

    private int mCornerRadius;

    public RoundClipFrameLayout(Context context) {
        this(context, null);
    }

    public RoundClipFrameLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundClipFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundClipFrameLayout, defStyleAttr, 0);
        mCornerRadius = ta.getDimensionPixelSize(R.styleable.RoundClipFrameLayout_cornerRadius, 0);

        ta.recycle();
    }

    public void setCornerRadius(int radiusPx) {
        if (mCornerRadius != radiusPx) {
            mCornerRadius = radiusPx;
            rebuildPath();
            invalidate();
        }
    }

    private void rebuildPath() {
        mPath.reset();
        mPath.addRoundRect(mRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
        mPath.close();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        super.onSizeChanged(width, height, oldWidth, oldHeight);

        mRect.set(0, 0, width, height);
        rebuildPath();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        int save = canvas.save();
        canvas.clipPath(mPath);
        super.dispatchDraw(canvas);
        canvas.restoreToCount(save);
    }
}
```

```attrs.xml
<resources>
    <declare-styleable name="RoundClipFrameLayout">
        <attr name="cornerRadius" format="dimension"/>
    </declare-styleable>
</resources>
```

# つかう

対象を囲うだけです。

```activity_main.xml
<com.example.widget.RoundClipFrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:cornerRadius="4dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/sample"/>

</com.example.widget.RoundClipFrameLayout>
```

![device-2016-09-16-141727.png](https://qiita-image-store.s3.amazonaws.com/0/40926/389c12cd-1744-542c-edab-105472eda2f1.jpeg)


# サンプル

https://github.com/ushi3/ExampleRoundClip

# 自分自身も角丸にしたい

上の実装では、子Viewしか角丸になりません。
当該 `View`の `background`も丸くしたいぞ！って場合は、`draw()`メソッドに同様の実装をすればOKです。

```
@Override
public void draw(Canvas canvas) {
    int save = canvas.save();
    canvas.clipPath(mPath);
    super.draw(canvas);
    canvas.restoreToCount(save);
}
```

# さらに楽な方法がありました

API Level 21で追加された、`ViewOutlineProvider`を使うことで、Viewの外からもclipping処理を割り込ませることができます。

```java:CircleOutlineProvider
public static class CircleOutlineProvider extends ViewOutlineProvider {

    @Override
    public void getOutline(View view, Outline outline) {
        outline.setRoundRect(
                0,
                0,
                view.getWidth(),
                view.getHeight(),
                radius);
        view.setClipToOutline(true);
    }
}
```

```
view.setViewOutlineProvider(new CircleOutlineProvider());
```


# 参考にしました

[Custom Layout that rounds the corners of its content - Stack Overflow](http://stackoverflow.com/a/32765455)

[googlesamples/android-ClippingBasic](https://github.com/googlesamples/android-ClippingBasic)

[ViewOutlineProvider を使う | Y.A.M の 雑記帳](http://y-anz-m.blogspot.com/2018/06/viewoutlineprovider.html)