Android 带圆角的自定义 WebView
Android Custom WebView With Rounded Corners
我正在尝试制作一个与常规 WebView 完全相同的自定义 WebView,只是它具有圆角。圆角需要透明,因为我想把这个 WebView 放在 Dialog 中。
我试着让我的习惯 class 像这样:
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(Canvas canvas)
中,您在末尾调用了 super
方法。这意味着您在自定义绘制方法中所做的任何事情都将被超级方法撤消。尝试先调用 super,然后再进行自定义绘图。
这是我找到的解决方案。在我的 onDraw() 方法中,我创建了一个倒置的、填充的、圆角的矩形,然后使用 Porter Duff Xfer 模式从屏幕 "clear" 该区域。这给我留下了一个具有漂亮斜边的 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;
}
}
它可能会对其他人有所帮助。在加载数据之前你需要设置
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);
}
}
这是@Luke 回答的 Kotlin 版本。
我还改进了代码以避免在 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)
}
我正在尝试制作一个与常规 WebView 完全相同的自定义 WebView,只是它具有圆角。圆角需要透明,因为我想把这个 WebView 放在 Dialog 中。
我试着让我的习惯 class 像这样:
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(Canvas canvas)
中,您在末尾调用了 super
方法。这意味着您在自定义绘制方法中所做的任何事情都将被超级方法撤消。尝试先调用 super,然后再进行自定义绘图。
这是我找到的解决方案。在我的 onDraw() 方法中,我创建了一个倒置的、填充的、圆角的矩形,然后使用 Porter Duff Xfer 模式从屏幕 "clear" 该区域。这给我留下了一个具有漂亮斜边的 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;
}
}
它可能会对其他人有所帮助。在加载数据之前你需要设置
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);
}
}
这是@Luke 回答的 Kotlin 版本。
我还改进了代码以避免在 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)
}