Android Custom WebView With Rounded Corners

馋奶兔 提交于 2020-06-26 07:45:03

问题


I'm trying to make a custom WebView that is completely identical to a regular WebView except that it has rounded corners. The rounded corners need to be transparent because I'd like to put this WebView in a Dialog.

I tried making my custom class like so:

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);
    }
}

and this implementation works for the most part. However, as soon as a url finishes loading and displays itself on the screen, I lose the rounded corners from the WebView. Any idea what's going on?


回答1:


Here was the solution I found. In my onDraw() method, I create an inverted, filled, rounded rectangle, and then use the Porter Duff Xfer Mode to "clear" that area from the screen. This leaves me with a WebView that has nicely bevelled edges, including the case where the WebView finishes loading a 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;
    }
}



回答2:


In onDraw(Canvas canvas) you call the super method at the end. That means that whatever you do in your custom draw method will be undone by the super method. Try calling super first and then doing your custom drawing.




回答3:


It might help others. Before loading data you need to set

webView.getSettings().setUseWideViewPort(true);

and applied you're drawable in XML file.

It worked for me.




回答4:


Here's the solution. After three day research.

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);

}
}



回答5:


Here is the Kotlin version of @Luke answer.

I also improved the code to avoid object allocations during the onDraw method.

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)
    }
}

Also here is the extension method to calculate dp to pixel:

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


来源:https://stackoverflow.com/questions/34299652/android-custom-webview-with-rounded-corners

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!