I have created CustomView. The view is higher in size than the screen. So it is showing fully in the device screen.
I want to determine position to determine how much it is outside the screen in left, right top and bottom. How to do that?
For what purpose you want to know how much of your view is outside of the screen? Can't you just use Scroller?
You can call getLocalVisibleRect on the view to get the "on screen" rectangle.
For example say in your onDraw
#Override
protected void onDraw(Canvas canvas) {
Rect r=new Rect();
getLocalVisibleRect(r);
// 0 to r.top is above the visible screen, etc
...
Once you have that rect it should be simple to calculate the values you want.
Related
I'm trying to implement a Waveform visualizer in my app which draws the waveform on a canvas from a real-time audio input. (Screenshot below)
What I need to do is to make it in a way that it can be scrolled horizontally from left to right.
I figured I should use a HorizontalScrollView and make the canvas gradually grow in width so that it spans over the screen width. Now I'm wondering how this can be achieved?
Keep the Canvas's width the same and use the canvas.translate() method when actually drawing.
So, let's say you have a Rect rect that contains the rectangle to draw (whose boundaries may exceed that of the View), as well as int horizontalOffset = 0 as the variable holding the offset of your drawing rectangle which will be adjusted as the user touches the View.
Now, when onDraw(Canvas) is called:
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.save();
canvas.translate(horizontalOffset, 0);
canvas.drawRect(rect, paint);
canvas.restore();
}
Then you can then override onTouch(View, MotionEvent) to calculate the horizontal offset based on the touch event and then invalidate() the View.
Note: this is clearly only drawing a Rect, but you should be able to modify what I did in there to draw whatever you're drawing. The important parts are (in order) save(), translate(), draw, restore().
I'm trying to create a custom AbsListView (overriding the same stuff on ListView, GridView and HeaderGridView) that will relocate all its drawing and touching events based on an external factor (other stuff that moves on the layout).
Paddings are not an option here, because then the only way to try to control the AbsListView position is using the super crappy method smoothScrollBy(int distance, int duration) and that is giving me a whole level of different issues that I'm trying to overcome by just all together drawing in a different area of the screen.
At the moment I found out that draw on a different area of the screen is as easy as:
#Override
protected void onDraw(Canvas canvas) {
canvas.translate(0, 400);
super.onDraw(canvas);
}
but the touch events don't get translated, and all the touches are 400 pixels up. So I've tried to translate the touch as well with:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
ev.offsetLocation(0, -400);
return super.dispatchTouchEvent(ev);
}
it does relatively work, to the point that 1) on item click get's called and 2) the view changes color based on the theme android:state_pressed="true", but the Up or Cancel touch evens never get called and the view does not refresh the drawing back to the default color.
I'll still further analyse, but I'm not sure if complex custom adapter with buttons and gesture detector, if that would work or not.
So the question:
How can I translate the drawing and still properly get touch events?
Right now, I have it so that my layout consists of a Fragment that takes up the entire screen and displays an image. I want to make it so that an additional View exists on top of it, also taking up the entire screen. On that top layer, I want to be able to color it all black initially, and then create certain spots that are transparent (alpha?) and reveal the image displayed on the fragment behind it. So basically the screen will be all black except for a few spots where the image behind is showing through, which I would determine programmatically. I've looked into a bunch of the graphics and views that Android provides, but have no clue where to start. Is this suited for a SurfaceView if I just want it to be all black with some spots of alpha?
Once I select the correct view to use, I'm assuming that I just override the onDraw() method and then do something like canvas.setBody(black) and then add shapes of alpha to it? Will the shapes correctly affect the background color?
For your masking View, you can make a custom view that can keep track of which areas to unmask (as Rects or something similar) and then draw them in onDraw(Canvas) like this:
public class MaskView extends View {
private Set<Rect> mRects = new HashSet<Rect>();
private Paint mUnmaskPaint = new Paint();
{
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
/**
* Add an unmasking rectangle to this view's background.
*
* #param rect
* a rectangle used to unmask the background
*/
public void addUnmaskRect(Rect rect) {
mRects.add(rect);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (Rect r : mRects) {
canvas.drawRect(r, mUnmaskPaint);
}
}
}
Your Fragment (or whatever is keeping track of the unmask areas) passes Rects to MaskView via addUnmaskRect(Rect). Whenever the view is redrawn (remember to call invalidate() each time you are done passing Rects) it is first filled with black, and then has any rectangles unmask the black background. The coordinates for your rectangles must be set to the coordinate space of the view, but if the underlying image occupies the exact same area it should be relatively simple (you can also look at the View.getLocationInWindow(int[]) method to help you with this as well).
I'm trying to override onDraw in an EditText subclass, to show a custom subtitle.
I've got it working, but there are a few bugs.
Basicall, all I need to do is draw StaticLayout at a certain offset from top left corner of the view.
Unfortunantly, all I get in my onDraw method is canvas. The size of the canvas is equal to the size of the whole screen (320x480 on a device with 320x480 display) and its clip bounds can be pretty much anything - it can be the whole view; it can be only top or bottom part of the view if the view is inside scrollview and partially visible; it can even be same arbitrary rect inside the view, probably because superclass invalidates only some of its region.
So if I have this view with size 320x48, I can get canvas with size 320x480 and clipping rect (200, 200, 300, 230) (left, top, right, bottom). I don't understand how this clipping rect maps to my view coordinates.
I need to know where is top left corner of the clipping rect relative to the top left corner of the view. Unfortunantly, I cannot figure out how to get this.
Added:
This code will work on all os versions that I've tested:
private int[] coordinates = new int[2];
private Matrix identityMatrix = new Matrix();
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
getLocationInWindow(coordinates);
canvas.setMatrix(identityMatrix);
canvas.translate(coordinates[0], coordinates[1]);
//do the drawing in EditText coordinate space
canvas.restore();
}
However, I still have one question: why does it work?
I've trying overriding View class and it's onDraw method will always recieve a canvas which size matches that of the View itself. Canvas for direct View subclass will have no clipping rect. Same for TextView (direct ancestor of the EditText class). But it's not the same for EditText. Canvas that gets passed to onDraw method of EditText will always (or not?) have the size of the screen, and a custom clipping rect. This whole "translate by view coordinates in window thing" seems very hacky. I don't understand why I should translate the coordinate space.
I've tried hacking android source code for the answers, but found none. EditText has no onDraw of its own. Theoretically, there should be no difference between overriding TextView onDraw and EditText onDraw. But there is a difference. The canvas object passed to onDraw method will be different depending on whether its TextView or EditText. Why? How do I know when I should apply transformation to matrix, and when I shouldn't?
you can have it with the View.getLocationOnScreen method
I am having a problem with visual artifacts on the screen when applying the 3D transformation found here. I have changed this so it rotates around the x axis instead of the y. When i do a full 180 rotation (top going away from you at first) im getting single pixel line artifacts at the bottom area (bottom 10-20%) of every other view that this is applied to. I am using a selector as the background of a LinearLayout and then applying this Animation to it. Can anyone think of a quick solution to this issue?
Thanks for any help!
Turns out you just have to invalidate the parent view on each animation step. If you have a custom Animation object you can just do this inside Animation.applyTransformation(...)
I had a similiar problem with a 2D animation where a View is moved off screen (outside parent view). My solution was quite simple. In my custom View I simply invalidate the parent view to have it redrawn at every frame.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
((View) this.getParent()).invalidate();
canvas.drawBitmap(icon, bm_x, bm_y, mPaint);
}