I am trying to draw rounded corner line using canvas. Like eg:
public class MyView extends View {
Paint paint;
Path path;
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
path.moveTo(50, 50);
path.lineTo(50, 500);
path.lineTo(200, 500);
path.lineTo(200, 300);
path.lineTo(350, 300);
float radius = 50.0f;
CornerPathEffect cornerPathEffect =
new CornerPathEffect(radius);
paint.setPathEffect(cornerPathEffect);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
}
}
I am able to draw corner line using paint as shown above, but not from the canvas.
I tried canvas.drawLine() and canvas.drawArc() to achieve the same result but failed to do that.
Can somebody help me to solve the issue?
In this example code I give a nice square in the form I want. canvas.drawRect (100, 300, 600, 800, paint); values work. But what I want is to call these values from the Activity class. So I want to send these values to the Draw class in the activity class. How can I do that ? For example, I want to send an activity class as drawRect (100,100,100,100, Color.BLUE). I do not want to write these values in the Draw class.
Draw.java
public class Draw extends View {
Paint paint;
Path path;
public Draw(Context context) {
super(context);
init();
}
public Draw(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Draw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void init(){
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawRect(100, 300, 600, 800, paint);
}
}
Activity.java
constraintLayout=findViewById(R.id.constraint);
Draw draw = new Draw(this);
constraintLayout.addView(draw);
You need to make method and pass value from activity to draw class:-
Draw draw = new Draw(this,100, 300, 600, 800);
constraintLayout.addView(draw);
Draw class
public class Draw extends View {
Paint paint;
Path path;
float left;
float top;
float right;
float bottom;
public Draw(Context context,float left, float top, float right, float bottom) {
super(context);
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
init();
}
public Draw(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Draw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void init(){
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawRect(left, top, right, bottom, paint);
}
}
You can create local variables for the bounds and set them using setters or init function before adding that view to a viewgroup.( constraintLayout.addView(draw) in your case
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?
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;
}
}
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.
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.
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);
}
}
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)
}
I am working with android.I had created an android app with a custom image view which clip corners of the image. here is my custom image view
public class CustomImage extends ImageView {
public static float radius = 18.0f;
public CustomImage(Context context) {
super(context);
}
public CustomImage(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
CustomImage.this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
Path clipPath = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
clipPath.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(clipPath);
super.onDraw(canvas);
}
}
it works perfect.But I need to add a border around the clipped Image view .How can I do this?
If you want rounded conners you can use this library RoundedImageView. You can see the code of library for you can implementation.
Or This tutorial Round Corners
I want to redraw a view from another method (say setProgress) in a class extending View. How can I do this.
My view class is
public class SpinView extends View {
private Paint paint;
private Context context;
private Canvas canvas;
private RectF arcOval;
public SpinView(Context context) {
super(context);
this.context = context;
}
public SpinView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
this.context = context;
}
public SpinView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
Display d = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = d.getWidth();
int height = d.getHeight();
arcOval = new RectF((width/2-50), (height/2-50), (width/2+50), (height/2+50));
// RectF arcOval = new RectF(200, 200, 300, 300);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
canvas.drawArc(arcOval, 270f, 360, false, paint);
}
protected void setProgress(float angle){
paint = new Paint();
paint.setColor(Color.YELLOW);
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
canvas.drawArc(arcOval, 270f, angle, false, paint);
}
}
All I want is to draw a circular arc of yellow color over the initially created arc of Blue color. Any help?
You can use invalidate() method to call ondraw again