Antialias on clipPath on layout - java

I have a layout, which has several views inside of it - toolbar, recyclerview and few separators (which are simple views with height of 2dp and match_parent width). I wanted to put a mask on a layout - most important part of it are the round corners for whole layout (and not the views itself). I decided to create my own LinearLayout class with overloaded dispatchDraw function and I managed to get nice result... except for one thing - those corners are not antialiased.
tl;dr is there ANY way to put antialias to clipPath function? I know can turn it on in Paint and then use xfermodes to mask the layout, however I have no clue how to draw mask and then draw everything else (without knowing what's exactly inside).
Here is my layout code (except for classname, simple constructors and Path field):
#Override protected void dispatchDraw(Canvas canvas) {
if (path == null) {
path = new Path();
float margin = MyApplication.getInstance().getMetrics().density * 5;
path.addRoundRect(new RectF(margin,margin,getWidth()-margin, getHeight()-margin),
margin*2, margin*2, Path.Direction.CW);
}
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

try this SO answer, of course instead of mMask.draw(canvas) which is a "mask" made by a NinePatchDrawable you would need to call canvas.drawPath() with a Paint set with PorterDuff.Mode.DST_IN xfer mode

Related

Java on android studio, why I cant see the Rectangle i drew in canvas?

Hi guys I recently started developing an android game, but i have encountered an issue with drawRectangle.
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
canvas.drawRect(new Rect(100,100,100,100), new Paint(Color.WHITE));
}
this Doesnt seem to work, but i have already drew on screen using another class with a draw method using the same logic but im curious why this doesnt work
private Paint myPaint = new Paint();
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
myPaint.setColor(Color.WHITE);
canvas.drawRect(new Rect(100,100,100,100), myPaint);
}
this doesnt work either
There are three issues in your implementation:-
Paint object doesn't take color in constructor, it takes a flag.
So you could have done something like Paint p = new Paint(Paint.ANTI_ALIAS_FLAG) and then set the color as p.setColor(Color.WHITE).
The Rect object should be something like new Rect(0,0,100,100).
In your case [new Rect(100,100,100,100)] the rectangle will be drawn as a rectangle with 0 width,
0 height and its upper left coordinate will be (100,100) and its
bottom right coordinate will be (100,100).
NEVER create objects in onDraw.
Paint's constructor doesn't take a color. It takes integer flags. So you just made a really weird paint object with every flag set, but you didn't set the color.
See: https://developer.android.com/reference/android/graphics/Paint.html#Paint(int)
This worked
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
myPaint.setColor(Color.WHITE);
canvas.drawRect(testRect, myPaint);
}
Where
private Rect testRect = new Rect(0,0,100,100}
private Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

Combine Two ContentView's

I am currently writing an app that will contain buttons and drawings. What I would like to do is combine the two onto the one screen.
setContentView(R.layout.activity_login_page);
setContentView(drawView);
}
class DrawView extends View {
Paint paint = new Paint();
public DrawView(Context context) {
super(context);
paint.setColor(Color.GREEN);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(50, 100, 200, paint);
canvas.drawLine(20, 10, 50, 20, paint);
}
}
}
When I set the content view to R.layout.activity_login_page I only get the button that I have added to the XML Layout file. When I have the two layouts being set as above, I only get the circle and the line appear and not the button. How can I combine the two?
in order to use your newly customized view within a layout, you need to do the following:
create all of the needed CTORs for your custom view class, or at least the one that includes the context and the attributes.
in the layout file (the xml file in res/layout) put the new view as a tag that includes its full path, or use the UI designer and drag and drop it (it's in the "custom views" category).
if you wish to handle your own customized attributes, you need to learn a bit more of it, and add attributes in the attr.xml file in the res/values folder, and also check the values in the CTOR (of the custom view).

How to overlay list item text color in specific region?

Let suppose we have the custom ListView which extends onDraw() method by drawing some rectangle in it.
class Listpicker extends ListView {
//..Some other methods here..//
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
}
}
The bitmap drawn represent some sort of glass which is rectangle with width equal to the width of listview and some height. What I want is when list item scrolls into this rectangle the color used for drawing text (not all item with background and etc.) would be changed into some another color. So, for example, when list item fits so that only half height of text fits into glassPickerBitmap, the outside part of list item should remain in its ordinal colors, and the part that is inside glassPickerBitmap should change its text color. List items are simple TextViews without any background.
How can be this achieved? Maybe any ColorFilters assigned to the Paint? But where? onDraw() method of Listview is not even called when ListView is scrolled... Can be this done inside customized views, for example, that will be ListView's items? Or may be some Color/Shader/do not know what else overlay can be used here, but where?
EDIT (added image examples):
Here is example of some crop from real-life app. There are 2 ListViews: one on the left side, other on the right. Glass is the grayed rectangle. Now, left list has "United States Dollar" currency selected. And I am scrolling right ListView in that way, that selection is somewhere between USD and Afghan Afghani. Now, what I want, is that USD currency in the left ListView would be drawn in red (exact color doesn't matter now, I will change it later to something meaningful) AND, in the same time, bottom part of "United States Dollar" and top part of "Afghan Afghani" in the right ListView would be drawn also in the same red color.
This color changing should be done in dynamic way - color should be changed only for the part of text that is under glass rectangle during scrolling of the list.
*OK, EUR and USD here are special currencies drawn with not standard cyan color. The question is about at least text with white color. But if it will be possible to change also cyan color it would be great.
As you can tell in their implementation they use and update a fixed position in their ListAdapter so then they update the View that matches accordingly, which is to me the easiest and simpler way to do it, but you don't get the half-half colored text, which I think you still want :)
So here is probably one of the worst answers I've given, sorry about it, I'll probably revisit this or hope someone can point you to a better solution
You can see a blurry demo at:
http://telly.com/Y98DS5
The dirty way:
Create a Paint with a proper ColorMatrixColorFilter, for the purposes of answering this I'm just desaturating, you'll have to figure out your 5x4 matrix for ColorMatrix to get the cyan your looking for. Also you will need some Rects to define source and dest when drawing below.
Create an off screen Bitmap and Canvas which you'll use to draw your list, that's done in onSizeChanged so we have proper view layout values, here you can start noticing the dirtiness as you will have to allocate a Bitmap as big as your ListView so you may get OutOfMemoryException.
Override draw to use your off screen Canvas and let ListView draw to it, then you can draw the Bitmap to the given Canvas that will draw your list as usual, then draw just the part of that Bitmap which will be drawn differently using the Paint we defined before, you will get overdraw for those pixels.
Finally draw your pre-fabricated glass Bitmap, I would recommend a n-patch (9-patch) so it scales and looks sharp across screens and densities, then you get more overdraw for those pixels.
The whole cake looks like:
public class OverlayView extends ListView {
private RectF mRect;
private Rect mSrcRect;
private Paint mPaint;
private Canvas mCanvas;
private Bitmap mBitmap;
private float mY;
private float mItemHeight;
public OverlayView(Context context) {
super(context);
initOverlay();
}
public OverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
initOverlay();
}
public OverlayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initOverlay();
}
private void initOverlay() {
// DO NOT DO THIS use attrs
final Resources res = getResources();
mY = res.getDimension(R.dimen.overlay_y);
mItemHeight = res.getDimension(R.dimen.item_height);
//
mRect = new RectF();
mSrcRect = new Rect();
mPaint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
mPaint.setColorFilter( new ColorMatrixColorFilter(colorMatrix) );
}
#Override
public void draw(Canvas canvas) {
super.draw(mCanvas);
canvas.drawBitmap(mBitmap, 0, 0, null);
canvas.drawBitmap(mBitmap, mSrcRect, mRect, mPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRect.set(0, mY, w, mY + mItemHeight);
mRect.roundOut(mSrcRect);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
So that said, I hope you end up with a cleaner solution, even if you don't get that half-half sexiness :)
First, keep a spare bitmap and canvas:
class Listpicker extends ListView {
Bitmap bitmapForOnDraw = null;
Canvas canvasForOnDraw = null;
Make sure it's the right size:
#override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
if (bitmapForOnDraw != null) bitmapForOnDraw.recycle();
bitmapForOnDraw = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvasForOnDraw = new Canvas(bitmapForOnDraw);
}
And when we come to drawing the list:
#override
protected void onDraw(android.graphics.Canvas realCanvas) {
// Put the list in place
super.onDraw(realCanvas);
// Now get the same again
canvasForOnDraw.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
super.onDraw(canvasForOnDraw);
// But now change the colour of the white text
canvasForOnDraw.drawColor(0xffff00ff, PorterDuff.Mode.MULTIPLY);
// Copy the different colour text into the right place
canvas.drawBitmap(bitmapForOnDraw, glassRect, glassRect overwritePaint);
// finally, put the box on.
canvas.drawBitmap(glassPickerBitmap, null, glassRect, semiTransparrentPaint);
}

Is it possible to combine different paths into a view or an object?

I created a drawing using different paths, but how can I move the entire drawing? How can I selected and move it around? Here is the main part of my onDraw method:
Path theSymbol = new Path();
theSymbol.moveTo(0.0F, 0.0F);
theSymbol.lineTo(0.0F, 50.0F);
theSymbol.lineTo(16.666666F, 58.333332F);
theSymbol.lineTo(-16.666666F, 75.0F);
theSymbol.lineTo(16.666666F, 91.666664F);
theSymbol.lineTo(-16.666666F, 108.33333F);
theSymbol.lineTo(16.666666F, 124.99999F);
theSymbol.lineTo(-16.666666F, 141.66666F);
theSymbol.lineTo(0.0F, 150.0F);
theSymbol.lineTo(0.0F, 200.0F);
theSymbol.offset(100.0F, 20.0F);
canvas.drawPath(theSymbol, paint);
That's how I draw a resistor to the screen (It works). Now I want some way of making all those paths be an object, something that I can select and move.
I have been looking at some projects like Sriracha, but I can't find how they are doing their element drawings.
I have also searched countless times, but all I get is "moving something on a path". Maibe I am searching for the wrong thing or this is not the way to do this kind of things.
I wold really appreciate if someone can point me in the right direction.
Put this drawing code into the onDraw() method of a custom View subclass. Then you can place your drawings around the screen however you like using layouts, animations, and other transformations just like any other view in the framework. Something like:
public class ResistorView extends View {
private Path mSymbol;
private Paint mPaint;
//...Override Constructors...
public ResistorView(Context context) {
super(context);
init();
}
public ResistorView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mSymbol = new Path();
mPaint = new Paint();
//...Your code here to set up the path,
//...allocate objects here, never in the drawing code.
}
//...Override onMeasure()...
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Use this method to tell Android how big your view is
setMeasuredDimension(width, height);
}
//...Override onDraw()...
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mSymbol, mPaint);
}
}
For more information about creating custom views, check out the SDK docs.
HTH

Dynamically change size of GridView

I'm developing an application for android, and i have a big old GridView that's centered vertically in the screen, with all the cells visible.
What i want, is a function by which all cells are zoomed in by a factor of 3, letting the user scroll around in the gridview instead.
Sort of how Wordfeud does it if you're familiar with the game.
I've searched the webs and haven't found a satisfactory solution, i managed to find a way to alter the layout params in my adapter for the grid tiles, but it only stretches them vertically and not horizontally, also the whole app becomes slow and unresponsive until you've scrolled through the list and let the Grid reload all the views or whatever.
Any help at all is appreciated.
In the end, i used a custom view with a Canvas instead - much easier than trying to bend GridView into something it's not supposed to be.
I know it's been a while since you asked, but I had a similar problem that it took me a long time to solve, so I'm hoping someone else may stumble on this. I ended up extending the GridLayout class and hooking up an instance of
private class ScaleGestureListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener
in the constructor:
scaleDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
since it's a view group, override the dispatch… instead of the on… methods:
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
scaleDetector.onTouchEvent(ev);
super.dispatchTouchEvent(ev);
return true;
}
#Override
public void dispatchDraw(Canvas canvas) {
String disp = String.valueOf(mScaleFactor);
Log.v(TAG, "dispatch draw " + disp);
canvas.save();
// i have some other stuff in here specific to my app
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}

Categories