带有圆角的Android自定义WebView



我正在尝试制作一个与常规WebView完全相同的自定义WebView,只是它有圆角。圆角需要透明,因为我想把这个WebView放在对话框中。

我试着把我的自定义类做成这样:

public class RoundedWebView extends WebView
{
    private Context context;
    private int width;
    private int height;
    public RoundedWebView(Context context)
    {
        super(context);
        initialize(context);
    }
    public RoundedWebView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initialize(context);
    }
    public RoundedWebView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        initialize(context);
    }
    private void initialize(Context context)
    {
        this.context = context;
    }
    // This method gets called when the view first loads, and also whenever the
    // view changes. Use this opportunity to save the view's width and height.
    @Override protected void onSizeChanged(int newWidth, int newHeight, int oldWidth, int oldHeight)
    {
        this.width = newWidth;
        this.height = newHeight;
        super.onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
    }
    @Override protected void onDraw(Canvas canvas)
    {
        int radius = Utilities.dpToPx(context, 5);
        Path clipPath = new Path();
        clipPath.addRoundRect(new RectF(0, 0, width, height), radius, radius, Path.Direction.CW);
        canvas.clipPath(clipPath);
        super.onDraw(canvas);
    }
}

并且这种实现在很大程度上起作用。然而,一旦url完成加载并显示在屏幕上,我就会丢失WebView中的圆角。知道发生了什么事吗?

这是我找到的解决方案。在我的onDraw()方法中,我创建了一个倒置、填充、圆角的矩形,然后使用Porter-Duff Xfer模式从屏幕上"清除"该区域。这给我留下了一个具有良好斜边的WebView,包括WebView完成加载url的情况。

public class RoundedWebView extends WebView
{
    private Context context;
    private int width;
    private int height;
    private int radius;
    public RoundedWebView(Context context)
    {
        super(context);
        initialize(context);
    }
    public RoundedWebView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initialize(context);
    }
    public RoundedWebView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        initialize(context);
    }
    private void initialize(Context context)
    {
        this.context = context;
    }
    // This method gets called when the view first loads, and also whenever the
    // view changes. Use this opportunity to save the view's width and height.
    @Override protected void onSizeChanged(int newWidth, int newHeight, int oldWidth, int oldHeight)
    {
        super.onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
        width = newWidth;
        height = newHeight;
        radius = Utilities.dpToPx(context, 5);
    }
    @Override protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        Path path = new Path();
        path.setFillType(Path.FillType.INVERSE_WINDING);
        path.addRoundRect(new RectF(0, getScrollY(), width, getScrollY() + height), radius, radius, Path.Direction.CW);
        canvas.drawPath(path, createPorterDuffClearPaint());
    }
    private Paint createPorterDuffClearPaint()
    {
        Paint paint = new Paint();
        paint.setColor(Color.TRANSPARENT);
        paint.setStyle(Style.FILL);
        paint.setAntiAlias(true);
        paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
        return paint;
    }
}

onDraw(Canvas canvas)中,您在末尾调用super方法。这意味着无论您在自定义绘制方法中做什么,都将被超级方法撤消。试着先调用super,然后再进行自定义绘图。

它可能会帮助其他人。在加载数据之前,您需要设置

webView.getSettings().setUseWideViewPort(true);

并应用您可以在XML文件中绘制。

它对我有效。

以下是解决方案。经过三天的研究。

public class RoundedWebView extends WebView
{
private final static float CORNER_RADIUS = 100.0f;
private Bitmap maskBitmap;
private Paint paint, maskPaint;
private float cornerRadius;
public RoundedWebView(Context context) {
    super(context);
    init(context, null, 0);
    initView(context);
}
public RoundedWebView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs, 0);
    initView(context);
}
public RoundedWebView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context, attrs, defStyle);
    initView(context);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    setWillNotDraw(false);
}
@Override
public void draw(Canvas canvas) {
    Bitmap offscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas offscreenCanvas = new Canvas(offscreenBitmap);
    super.draw(offscreenCanvas);
    if (maskBitmap == null) {
        maskBitmap = createMask(canvas.getWidth(), canvas.getHeight());
    }
    offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
    canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
}
private Bitmap createMask(int width, int height) {
    Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
    Canvas canvas = new Canvas(mask);
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.WHITE);
    canvas.drawRect(0, 0, width, height, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);
    return mask;
}
void initView(Context context){
    // i am not sure with these inflater lines
    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    // you should not use a new instance of MyWebView here
    // MyWebView view = (MyWebView) inflater.inflate(R.layout.custom_webview, this);
    this.getSettings().setUseWideViewPort(true);
    this.getSettings().setLoadWithOverviewMode(true);
}
}

这是Kotlin版本的@Luke答案。

我还改进了代码,以避免在onDraw方法中分配对象。

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RectF
import android.util.AttributeSet
import android.webkit.WebView
import net.onefivefour.android.bitpot.extensions.dpToPx

class RoundedWebView : WebView {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    private lateinit var roundedRect: RectF
    private val cornerRadius = 10f.dpToPx(context)
    private val pathPaint = Path().apply {
        fillType = Path.FillType.INVERSE_WINDING
    }
    private val porterDuffPaint = Paint().apply {
        color = Color.TRANSPARENT
        style = Paint.Style.FILL
        isAntiAlias = true
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    }
    override fun onSizeChanged(newWidth: Int, newHeight: Int, oldWidth: Int, oldHeight: Int) {
        super.onSizeChanged(newWidth, newHeight, oldWidth, oldHeight)
        roundedRect = RectF(0f, scrollY.toFloat(), width.toFloat(), (scrollY + height).toFloat())
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        pathPaint.reset()
        pathPaint.addRoundRect(roundedRect, cornerRadius, cornerRadius, Path.Direction.CW)
        canvas.drawPath(pathPaint, porterDuffPaint)
    }
}

这里还有计算dp到像素的扩展方法:

fun Float.dpToPx(context: Context): Float {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, context.resources.displayMetrics)
}

最新更新