18

汎用のViewGroupを作成し、XMLレイアウトで再利用して、そこに配置されたものの隅を丸めることができます。

どういうcanvas.clipPath()わけか効果がないようです。私は何が間違っているのですか?

Javaコードは次のとおりです。

package rounded;

import static android.graphics.Path.Direction.CCW;
public class RoundedView extends FrameLayout {
    private float radius;
    private Path path = new Path();
    private RectF rect = new RectF();

    public RoundedView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.radius = attrs.getAttributeFloatValue(null, "corner_radius", 0f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int savedState = canvas.save();
        float w = getWidth();
        float h = getHeight();
        path.reset();
        rect.set(0, 0, w, h);
        path.addRoundRect(rect, radius, radius, CCW);
        path.close();
        boolean debug = canvas.clipPath(path);
        super.onDraw(canvas);
        canvas.restoreToCount(savedState);
    }
}

XMLでの使用法:

<?xml version="1.0" encoding="utf-8"?>
<rounded.RoundedView   xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    corner_radius="40.0" >
    <RelativeLayout 
        android:id="@+id/RelativeLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        ...
    </RelativeLayout>
</rounded.RoundedView>
4

7 に答える 7

55

ViewGroupその子をクリップする正しい方法は、dispatchDraw(Canvas)メソッドでそれを行うことです。

ViewGroupこれは、aの子を円でクリップする方法の例です。

private Path path = new Path();

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    // compute the path
    float halfWidth = w / 2f;
    float halfHeight = h / 2f;
    float centerX = halfWidth;
    float centerY = halfHeight;
    path.reset();
    path.addCircle(centerX, centerY, Math.min(halfWidth, halfHeight), Path.Direction.CW);
    path.close();

}

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

このdispatchDrawメソッドは、子をクリップするために呼び出されるメソッドです。setWillNotDraw(false)レイアウトが子をクリップするだけの場合は必要ありません。

この画像は上記のコードで取得されています。Facebookを拡張しましたProfilePictureView( Facebookのプロフィール写真FrameLayoutを含む正方形を含みます)。ImageView

サークルクリッピング

したがって、丸い境界線を実現するには、次のようにします。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    // compute the path
    path.reset();
    rect.set(0, 0, w, h);
    path.addRoundRect(rect, radius, radius, Path.Direction.CW);
    path.close();

}

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

ラウンドボーダークリッピング

実際には、複雑なパスを作成できます:)

「Op」操作でclipPathを複数回呼び出すことができ、複数のクリッピングを好きなように交差させることができます。

注:でパスを作成したのは、パフォーマンスに悪影響をonSizeChanged与えるためです。onDraw

注2:パスのクリッピングはアンチエイリアシングなしで実行されます:/したがって、滑らかな境界線が必要な場合は、他の方法で行う必要があります。現在、クリッピングでアンチエイリアシングを使用する方法を知りません。

UPDATE(概要)

Android Lollipop(API 21)以降、標高と影をビューに適用できます。アウトラインと呼ばれる新しい概念が導入されました。これは、シャドウやその他(波及効果など)の計算に使用されるビューの形状をフレームワークに指示するパスです。

ビューのデフォルトOutlineは、ビューのサイズの長方形ですが、楕円形/円または丸みを帯びた長方形にすることができます。カスタムを定義するには、ビューでOutlineメソッドを使用する必要があります。カスタムビューの場合は、カスタムビューの内部クラスとして定義されsetOutlineProvider()たカスタムを使用して、コンストラクターでカスタムを設定できます。凸状のパスである限り、任意のプロバイダーを使用しViewOutlineProviderて独自のプロバイダーを定義できます(数学的な概念とは、凹みや穴のない閉じたパスを意味します。たとえば、星型も歯車型も凸型ではありません)。OutlinePath

このメソッドsetClipToOutline(true)を使用して、アウトラインもクリップすることもできます(これはアンチエイリアシングでも機能すると思いますが、誰かがコメントで確認/反論できますか?Path )が、これは非アウトラインでのみサポートされます。

幸運を

于 2015-09-24T15:36:31.597 に答える
23

draw(Canvas canvas)メソッドをオーバーライドできます。



    public class RoundedLinearLayout extends LinearLayout {
        Path mPath;
        float mCornerRadius;

        public RoundedLinearLayout(Context context) {
            super(context);
        }

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

        public RoundedLinearLayout(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        @Override
        public void draw(Canvas canvas) {
            canvas.save();
            canvas.clipPath(mPath);
            super.draw(canvas);
            canvas.restore();
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            RectF r = new RectF(0, 0, w, h);
            mPath = new Path();
            mPath.addRoundRect(r, mCornerRadius, mCornerRadius, Direction.CW);
            mPath.close();
        }

        public void setCornerRadius(int radius) {
            mCornerRadius = radius;
            invalidate();
        }
      }

于 2013-08-16T16:29:59.320 に答える
3

Ur Layoutの背景が設定されていない場合、 FrameLayoutのonDrawは呼び出されません。

dispatchDrawをオーバーライドする必要があります。

于 2015-12-23T09:13:06.053 に答える
2

ViewGroup(したがってそのサブクラス)は、デフォルトで描画を行わないことを示すフラグを設定します。ソースでは、次のようになります。

// ViewGroup doesn't draw by default
if (!debugDraw()) {
    setFlags(WILL_NOT_DRAW, DRAW_MASK);
}

だからあなたonDraw(...)はおそらく今はまったくヒットしないでしょう。手動描画を行う場合は、を呼び出しますsetWillNotDraw(false)

于 2012-11-22T18:44:25.997 に答える
2

setWillNotDraw(false);で呼び出されるonDrawを要求することを忘れないでください。値をmRadiusに設定してから、次のようにします。

@Override
protected void onDraw(Canvas canvas) {
    mPath.reset();
    mRect.set(0, 0, canvas.getWidth(), canvas.getHeight());
    mPath.addRoundRect(mRect, mRadius, mRadius, Direction.CCW);
    mPath.close();
    canvas.clipPath(mPath);
}
于 2014-01-13T19:50:09.173 に答える
-2

childViewsをクリップするには、drawChild()メソッドをオーバーライドする必要があります。

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    int flag = canvas.save();
    canvas.clipPath(pathToClip);
    boolean result=super.drawChild(canvas, child, drawingTime);
    canvas.restoreToCount(flag);
    return result;
}

ViewGroupの背景もクリップしたい場合は、代わりにdraw()をオーバーライドしてください。

@Override
public void draw(Canvas canvas) {
    int flag = canvas.save();
    canvas.clipPath(pathToClip);
    super.draw(canvas);
    canvas.restoreToCount(flag);
}
于 2013-11-13T05:15:39.910 に答える
-3

レイアウトの背景としてShapeDrawableを定義し、実行時にドローアブルのコーナー半径を変更するだけではどうでしょうか。

于 2012-11-22T17:10:59.230 に答える