Given a closed Path object result is like this:
Although that is a rectangle I'm looking for something which works with any closed Path.
While steelbytes' answer will probably give you more control over the individual sections of the gradient, you can do it without the path:
Paint m_Paint = new Paint();
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// start at 0,0 and go to 0,max to use a vertical
// gradient the full height of the screen.
m_Paint.setShader(new LinearGradient(0, 0, 0, getHeight(), Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR));
canvas.drawPaint(m_Paint);
}
this may help.
Note: it's not efficient to create the Paint etc in every call to onDraw. This is just an demonstration of LinearGradient shader
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
int w = getWidth();
int h = getHeight();
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
Path pth = new Path();
pth.moveTo(w*0.27f,0);
pth.lineTo(w*0.73f,0);
pth.lineTo(w*0.92f,h);
pth.lineTo(w*0.08f,h);
pth.lineTo(w*0.27f,0);
p.setColor(0xff800000);
p.setShader(new LinearGradient(0,0,0,h,0xff000000,0xffffffff,Shader.TileMode.CLAMP));
canvas.drawPath(pth,p);
}
Related
By having the following codes, I have some questions.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( new View(this) {
Paint mPaint = new Paint();
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int width = this.getWidth();
int height = this.getHeight();
int radius = width > height ? height/2 : width/2;
int center_x = width/2;
int center_y = height/2;
// prepare a paint
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(true);
// draw a rectangle
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL); //fill the background with blue color
canvas.drawRect(center_x - radius, center_y - radius, center_x + radius, center_y + radius, mPaint);
// draw some text and rotation
mPaint.setTextSize(50);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(Color.BLACK);
canvas.drawText( "Hello World" , center_x , center_y, mPaint);
}
});
}
}
Q1: How can I fill blue colour in the frame? (The words still appear)
Q2: How many views and surfaces in this app? How can I count these in the app?
Q3: How many windows in this app?
Q4: In the code, I dont see any bitmap object in it.
However, I thought that bitmap is the object that I can really draw things on it. Is my
understanding incorrect?
One possibility is that Canvas constructor initializes bitmap when it is newed.
Q5: I knew that these graphic thing will finally go to surface and then pass to
surfaceflinger for final composition. Where does it locate in my code?
Thanks for any reply.
Five questions. Let's see where I can help.
Q1: Tell the Paint to fill the rectangle: paint.setStyle(Paint.Style.FILL);
Q2: I see only the one view you create programmatically. Why would you like to count the views?
Q3: Again: One
Q4: You draw an mutable bitmaps by wrapping them with a Canvas. The method to actually draw are part of Canvas
Q5: The code you show is part of an Activity. The Activity is called by Android. It's your entry point into your App.
Thanks for the answer. I did the job of making the code for marked answer, and it works.
Bitmap bg = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bg);
// paint background with the trick
Paint rect_paint = new Paint();
rect_paint.setStyle(Paint.Style.FILL);
rect_paint.setColor(Color.rgb(0, 0, 0));
rect_paint.setAlpha(0x80); // optional
canvas.drawRect(0, 0, width, height, rect_paint); // that's painting the whole canvas in the chosen color.
Q2:Hierarchy Viewer is very useful when you want to count how many views in your app.
Optimizing Your UI
What steps are required to create a shape e.g. rectangle with a shadow from scratch using a Canvas?
Adding a shadow layer to the paint used to draw the rectangle yielded no success.
No need for a Bitmap, just needed to set the layer type to LAYER_TYPE_SOFTWARE the original approach worked.
public class TestShapeShadow extends View
{
Paint paint;
public TestShapeShadow(Context context)
{
super(context);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShadowLayer(12, 0, 0, Color.YELLOW);
// Important for certain APIs
setLayerType(LAYER_TYPE_SOFTWARE, paint);
}
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawRect(20, 20, 100, 100, paint);
}
}
I followed the ideas in #pskink's answer and found a solution.
I put the code snippet here for anyone in need.
public class MyViewWithShadow extends View {
Paint paint;
int mainColor;
int shadowColor;
// shadow properties
int offsetX = -25;
int offsetY = 30;
int blurRadius = 5;
public MyViewWithShadow(Context context)
{
super(context);
mainColor = Color.RED;
shadowColor = Color.BLACK; // this color can also have alpha
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas)
{
// Create paint for shadow
paint.setColor(shadowColor);
paint.setMaskFilter(new BlurMaskFilter(
blurRadius /* shadowRadius */,
BlurMaskFilter.Blur.NORMAL));
// Draw shadow before drawing object
canvas.drawRect(20 + offsetX, 20 + offsetY, 100 + offsetX, 100 + offsetY, paint);
// Create paint for main object
paint.setColor(mainColor);
paint.setMaskFilter(null);
// Draw main object
canvas.drawRect(20, 20, 100, 100, paint);
}
}
[Link is now broken:]
If you wonder what shadow properties are, you can refer to this tester:
https://okawa-h.github.io/box-shadow_tester/~
create. a Path, add some elements to it
set BlurMaskFilter to a Paint
draw a path with dx, dy shadow offset
unset mask filter
draw a path again with no. offset
Am drawing a simple circle in a Canvas on Android.
Now I need to fill it with some gradient colors, but they are no really smooth.
This its what i have done:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rectF.set(20, 20, this.get_Width() - this.get_Width()/10, this.get_Width() - this.get_Width()/10);
RadialGradient gradient = new RadialGradient(200, 200, 200, 0xFFFFFFFF,
0xFF000000, android.graphics.Shader.TileMode.CLAMP);
Paint mPaint = new Paint();
mPaint.setDither(true);
mPaint.setShader(gradient);
canvas.drawCircle(getWidth()/2, getHeight()/2, getWidth()/3, getGradient());
invalidate();
}
And this is the result:
My question is: Is it there some way to make HQ radial gradients?
Try adding the following to your Activity which contains the View that does the drawing:
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
Window window = getWindow();
// Eliminates color banding
window.setFormat(PixelFormat.RGBA_8888);
}
Finally I should initialize my Paint object as:
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
That makes the borders cleaner.
And with the #Waqas answer, make the colors itself smoothers.
I'm overriding Android's ImageView in order to make the corners of my image transparent. I can accomplish that clipping the canvas in my onDraw(Canvas canvas):
#Override
protected void onDraw(Canvas canvas) {
Path clipPath = new Path();
int w = this.getWidth();
int h = this.getHeight();
clipPath.addRoundRect(new RectF(0,0,w,h), 10.0f, 10.0f, Path.Direction.CW);
canvas.clipPath(clipPath);
super.onDraw(canvas);
}
Unfortunately, it's not possible to antialias this round rectangle, and the result are ugly corners like this:
I know I can clear parts of my canvas with antialiasing using Paint and PorterDuff.Mode.CLEAR, what I don't know is to specify the round corners as the region to be erased. I'm looking for something like this:
#Override
protected void onDraw(Canvas canvas) {
//superclass will draw the bitmap accordingly
super.onDraw(canvas);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//this will erase a round rectangle, I need the exact inverse
canvas.drawRoundRect(rect, rx, ry, paint);
}
Is there any way to "erase" not the round rectangle, but it's inverse, ie, the round corners? And what if I just want to erase one of the corners?
Draw using a BitmapShader with a transparent color for your Paint object.
If you just want to erase one of the corners, try drawing it as a Path instead of a RoundRect.
protected void onDraw(Canvas canvas) {
BitmapShader bitmapShader = new BitmapShader(<original drawable>, TileMode.CLAMP, TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(0xFF000000);
paint.setShader(bitmapShader);
canvas.drawRoundRect(rect, rx, ry, paint);
}
I need to rotate an ImageView by a few degrees. I'm doing this by subclassing ImageView and overloading onDraw()
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.scale(0.92f,0.92f);
canvas.translate(14, 0);
canvas.rotate(1,0,0);
super.onDraw(canvas);
canvas.restore();
}
The problem is that the image that results shows a bunch of jaggies.
http://img.skitch.com/20100608-gmdy4sexsm1c71di9rgiktdjhu.png
How can I antialias an ImageView that I need to rotate in order to eliminate jaggies? Is there a better way to do this?
If you know that your Drawable is a BitmapDrawable, you can use anti-aliasing in the bitmap's Paint to do something like the following:
/**
* Not as full featured as ImageView.onDraw(). Does not handle
* drawables other than BitmapDrawable, crop to padding, or other adjustments.
*/
#Override
protected void onDraw(Canvas canvas) {
final Drawable d = getDrawable();
if( d!=null && d instanceof BitmapDrawable && ((BitmapDrawable)d).getBitmap()!=null ) {
final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
canvas.save();
// do my rotation and other adjustments
canvas.scale(0.92f,0.92f);
canvas.rotate(1,0,0);
if( paddingLeft!=0 )
canvas.translate(paddingLeft,0);
if( paddingTop!=0 )
canvas.translate(0,paddingTop);
canvas.drawBitmap( ((BitmapDrawable)d).getBitmap(),0,0,p );
canvas.restore();
}
}
Set antialias and filterBitmap to the paint that draw's the bitmap.
I had a similar problem and this worked for me:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
I did the following to get it to work:
In AndroidManifest.xml I enabled hardware-acceleration <application android:hardwareAccelerated="true">.
Instead of using a custom draw-method, I changed the layer-type of the parent-view to hardware-accelerated with anti-aliasing enabled and added my subviews as follows:
Paint layerPaint = new Paint();
layerPaint.setAntiAlias(true);
layerPaint.setFilterBitmap(true);
layerPaint.setDither(true);
RelativeLayout parentLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.myLayout, null);
parentLayout.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
parentLayout.addView(myChildView);
I have to add that clipping cannot be disabled when using this approach.
This rotation solves just one part of problem - jagged edges, but the image instead became wierd.
ifound only one solution yet:
in ondraw or dispatch draw method
Bitmap bmp = ((BitmapDrawable)image.getDrawable()).getBitmap();
double scale = (0.0+bmp.getWidth()+40.0)/getWidth();
if (scale > 1){
bmp = Bitmap.createScaledBitmap(bmp, getWidth()-40, (int) (bmp.getHeight()/scale), true);
}
canvas.drawColor(Color.TRANSPARENT);
canvas.save();
canvas.translate(20, yShift);
int left = borderSize;
int top = borderSize;
int right = bmp.getWidth() - borderSize;
int bottom = bmp.getHeight() - borderSize;
if(showText){
mPaint.setColor(Color.WHITE);
canvas.rotate(getAngel());
canvas.drawRect(left, top, right, bottom, mPaint);
canvas.translate(0,0);
textFrame.draw(canvas);
}else{
// rotaed bitmap
mMatrix.setRotate(getAngel());
// draw bitmap after creation of new rotated bitmap from rotated matrix
canvas.drawBitmap(Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mMatrix, true)
, 0, 0, mPaint);
}
canvas.restore();
#Primoz990's solution worked for me :) I also enabled Dithering, so my final code which removes the jagged edges on rotation is this:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
bitmapPaint.setDither(true);