I've created a bar that is supposed to contain multiple views of different colors. A line has to be drawn that indicates the current position in the bar. Below is my simplified code:
public class Abar extends LinearLayout {
final Paint line_paint = new Paint();
private Context context;
public Abar(Context context) {
super(context);
this.context = context;
line_paint.setColor(Color.WHITE);
setWeightSum(2000);
setBackgroundResource(R.color.blue);
View view = new View(context);
view.setBackgroundResource(R.color.yellow);
this.addView(view, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1000));
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(20, 80, 100, 80, line_paint);
}
}
Now, this does not seem to work. No matter if I swap the canvas.drawLine with super.onDraw, the line is not visible unless I remove the view.setBackgroundResource. How can I draw a line over the LinearLayout. I'd rather not use FrameLayout if possible.
Below are pictures of what I'm trying to achieve (adding a white line) on top of the bars (note: the white bar on the first picture is just exaggerated big for clearness on SO):
Override dispatchDraw() instead of onDraw(). You want to draw the line after the child views draw (which isn't in super.onDraw).
Related
I have a frame layout (full screen) that acts as a container for another frame layout which shows the camera preview. Now I want to show on top of the camera preview a circle of a given radius. The radius shall change as a function of some properties of the current preview image.
I know that I can add some (semi-transparent) layouts "on top" of the frame layout I use for the camera preview, but I am not sure how to best show this "overlay" circle. The circle shall be centered, have a solid line but not be filled, i.e. be transparent and also its background shall be transparent.
You can make your own View class inside your existing Activity class. Here is my class.
class CanvasView extends View {
private Paint paint;
public CanvasView(Context context){
super(context);
//Customize your own properties
paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10f);
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
paint.setColor(Color.RED);
canvas.drawCircle(enter coords and radius here);
}
}
You can call the onDraw method from within that class by calling the invalidate() method..
Here is how you can add it to your layout..
Assuming you declared a CanvasView class, called drawable, and have your Framelayout, called main_layout, you add it to your existing framelayout by doing...
CanvasView drawable = new CanvasView(getApplicationContext());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
main_layout.addView(drawable, params);
Hope this works!
I'm trying to prevent over-draw in my app, and in one of the root views I'd like to detect the location of my children views, and then draw a background for where they are not. From my understanding, clipping would clip outside of the path, but what I'd really like to do is not draw at certain locations on the screen where the children are. Is there a good way to do this?
EDIT:
So I haven't played with canvas in a while, but I'm looking to do something like this:
public class ContainerView extends FrameLayout {
private final Paint mBackgroundPaint = new Paint();
private final Path mPath = new Path();
public ContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
mBackgroundPaint.setColor(context.getResources().getColor(R.color.default_background));
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPaint(mBackgroundPaint);
mPath.reset();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
mPath.addRect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom(),
Path.Direction.CCW); // What direction do I want?
}
canvas.clipPath(mPath);
}
}
clipPath just wont do what I want though, I don't think. I think that would clip outside the path, and I need to clip inside the path.
You'd probably want to do something like override onLayout and at the end of it:
iterate over all your child views
check if the child is visible/opaque
build up a list of each child's position/size
use that list to do your own background clipping
I'm quite new to Android programming and I'm stuck on how to get this right.
My situation is I want to draw a rectangle inside a RelatativeLayout and be able to change the shape of the rectangle dynamically relative to the size of the RelativeLayout which does not encompass the whole screen.
My problem is when I try to implement this, the size of the rectangle is still relative to the screen size
Is there a way where I can restrict the canvas size to the RelativeLayout size or is there a better way?
Here is part of my code from the maim activity
RelativeLayout.LayoutParams rl = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
blueView = new Bar(this);
blueView.setLayoutParams(rl);
blueLayout.addView(blueView);
And I also have a Bar class that extends View
public class Bar extends View{
Rect theBar;
public Bar(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
theBar = new Rect();
theBar.set(0, 0, canvas.getWidth(), canvas.getHeight()/2);
Paint blue = new Paint();
blue.setColor(Color.BLACK);
blue.setStyle(Paint.Style.FILL);
canvas.drawRect(theBar, blue);
}
}
I am using the onDispatchDraw(Canvas canvas) method to draw lines in my view. When I call canvas.drawLine() it always draws the line on top of all my views. Is there any way to draw the line under a button but on top of another view in my layout using canvas.drawLine()?
I have tried the following but it still draws the line over the button.
Button b;
RelativeLayout r;
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
Paint p = new Paint();
p.setColor(Color.Black);
canvas.drawLine(0,0,100,100,p);
r.removeView(b);
r.addView(b);
}
You're trying to reinvent the wheel. Z-ordering is already implemented in the window management subsystem and you can use it.
Create a custom view you want to draw on.
Make it non-clickable using android:clickable="false" or setClickable(false).
Make its background transparent and implement dispatchDraw().
Put all the views you don't want to draw on above this view in the view hierarchy.
Call super.dispatchDraw() after drawing the line. The dispatchDraw is called by viewgroup for drawing its children, so in your case, calling super.dispatchDraw() will draw the button first then you are drawing the line over it. Do dispatchDraw this way :
Updated code
class myListViewWithLine extends ListView {
....
#Override
protected void dispatchDraw(Canvas canvas) {
Paint p = new Paint();
p.setColor(Color.Black);
canvas.drawLine(0,0,100,100,p);
super.dispatchDraw(canvas);
}
....
}
Another method will be to just add a view with 1 dip as height and background color whatever you like under the button. It should look like like a line.
View v = new View(this);
ViewGroup.LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT,1);
v.setLayoutParams(lp);
v.setBackgroundColor(Color.WHITE);
addView(v,INDEX NUMBER AFTER THE BUTTON);
I am very new to Android and I am having problems figuring out how to layout views within a RelativeLayout programmatically. What I want to do is create 4 circles (child views) with a certain radius (say 50px) in the center of the RelativeLayout container, so it would look like I have an imaginary square in the center of the RelativeLayout and each vertex is the center for one of the circles.
I am able to draw the circle in the view; that is simple enough :)
class CircleView extends View {
...
protected void onDraw(Canvas canvas) {
// draw circle on canvas
}
}
What I cannot figure out is how to layout the views. It seems to draw them on top of each other, even though I am setting LayoutParams and an Id for each of the child views.
class Circles extends RelativeLayout {
public Circles(Context c) {
super(c);
addChildViews();
}
...
private void addChildViews() {
final Context c = getContext();
final CircleView v0 = new CircleView(c);
v0.setId(0);
final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.LEFT_OF, 1);
params.addRule(RelativeLayout.ABOVE, 2);
v0.setLayoutParams(params);
addView(v0);
....
// and so on, with relative layout params for other 3 views
}
}
Can somebody put me on the right track please? I also don't know if I am not calling addChildViews at the right time in the drawing cycle, and if this is what is leading to them being drawn on top of each other. Many thanks for any help.
Two things
1)The default action of a View is to fill its parent, so by applying (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) as your LayoutParams, you're creating four views with heights and widths the size of your parent layout (presumably the screen), so you'd only see one as the others would be positioned offscreen.
To fix this, either set the size you want the circles to be as your LayoutParms,
float dpi = getResources().getDisplayMetrics().density;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams((int)(50.0f * dpi), (int)(50.0f * dpi));
or you could override the onMeasure(int x, int y) method in your Circle View like so
#Override
public void onMeasure(int x, int y) {
float dpi = getResources().getDisplayMetrics().density;
setMeasuredDimension((int)(50.0f * dpi), (int)(50.0f * dpi));
}
2) Don't set your View id to 0...I can't remember if it's system reserved or what, but it doesn't play nice.
Additionally, if you want all the circles centered in your Circles, you'll want to set its gravity to center like so.
public class Circles extends RelativeLayout {
public Circles (Context ctx) {
super(ctx);
this.setGravity(Gravity.CENTER);
addViewChildren();
}
This will center all of the Circles children, giving you the desired result.