问题
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