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.
Related
I'm making a custom View thats derived from an ImageViewand I control where the image has to be within this ImageView using padding.
I have set OnClickListener on my custom ImageView that resizes it:
image.setOnClickListener(new ImageView.OnClickListener(){
#Override
public void onClick(View v) {
image.resize_image();
}
});
and this is how this function is looking like
public void resize_image(){
ViewGroup.LayoutParams params = getLayoutParams();
params.height = new_heigth;
params.width = new_width;
setLayoutParams(params);
}
After this resizing is done I don't want my displayed image to change size (only the custom ImageView is changing size so I can draw an extra stuff around this image) within my custom ImageView so inside onDraw(Canvas) member function I set the new padding
class custom_ImageView extends ImageView{
//...
#Override
protected void onDraw(Canvas canvas) {
//...
setPadding(new_left_padding, new_top_padding, new_right_padding, new_bottom_padding);
//...
}
//...
}
Result is that width and heigth are changed like I wanted but my displayed image is neither in the right position or size.
Interesting this is that if I add an extra line of invalidate(); in the end of my resize_image() and I click on my custom_ImageView twice - on the 2nd click image draws itself in right size and position like I wanted.
Can anyone tell me why this is happening?
I kind of worked around this by not using ImageView at all but just drawing image where it needs to be by using Canvas.drawBitmap(Bitmap, RectF, RectF, Paint)
This way I don't have to specify images location using setPadding within onDraw, but I can do it simply by specifying position using RectF.
My question is about difference in setting the coordinates of the center a circle using the half of the view and directly. My device is 240 * 320 and when I use the getWidht()/2 and the getHeight() methods to draw a circle in center of the screen I am successful. But when I use the 120 value instead the getWidth/2 and the 160 value instead the getHight()/2 , I can not draw the circle in center of the screen. while I think the getWidth()/2 value is equal to the 120 value and the getHeight()/2 value is equal to the 160 value.
public class MainActivity extends Activity {
RelativeLayout relativeLayout;
C c;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
relativeLayout = (RelativeLayout) findViewById(R.id.re);
c = new C(getApplicationContext());
relativeLayout.addView(c);
}
}
class C extends View {
Paint paint;
C(Context context) {
super(context);
paint = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
paint.setAntiAlias(true);
paint.setColor(Color.RED);
canvas.drawCircle(120,160,20,paint);//this is not in the center of the screen.
canvas.drawCircle(getWidth/2, getHeight/2, 20 , paint);// But this is in center of the screen
}
}
Did you print the getWidth and getHeight? It maybe not 160 and 120, because it according to your views size, sometime the size it will reduce the status bar on some Android device...
I solved my problem. because the relativeLayout object has padding attribute. I should remove the padding attributes from the relativeLayout object. For more information please see here.
I am looking for a solution to have larger bounds for selector of an Android view.
Imagine that View is a rectangle view. When user click on the view touch feedback that will be shown is a circle pretty larger than view bounds.
Do you know a good approach to build such an idea?
There is no way how to do that in view's bounds.(I think ). You can implement it by making visual action on parent background view.
ImageButton im = (ImageButton) findViewById(R.id.ur_btn);
im.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
float x = im.getX();
float y = im.getY();
}
});
}
private void addCircleBehindRectangle(int x, int y){
RelativeLayout parent = (RelativeLayout) findViewById(R.id.my_relative_layout);
ImageView iv = new ImageView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(x, y);
//optional margin l/r
params.leftMargin = 50;
params.topMargin = 60;
parent.addView(iv, params);
}
I hope it helps a little bit. Good luck
If you want to show a view outside of its clipping area, you need to enable this in its parent container (any layout which derives from ViewGroup):
ViewGroup.clipChildren
Another way is increase/decrease paddings of the clicked view itself.
I am developing a custom ViewGroup that contains a number of views. In the constructor, I create a number of FrameLayouts that will each contain two ImageViews. Each Framelayout and the child views are created using the following code.
FrameLayout frame = new FrameLayout(context);
ImageView digitView = new ImageView(context);
digitView.setScaleType(ScaleType.CENTER_CROP);
frame.addView(digitView,
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
ImageView bezelView = new ImageView(context);
bezelView.setScaleType(ScaleType.CENTER_CROP);
bezelView.setImageResource(R.drawable.bezel);
frame.addView(bezelView);
this.addView(frame, params);
In the onMeasure callback I set the dimensions of the custom ViewGroup
this.setMeasuredDimension(254, 43);
and in the onLayout callback, I calculate the dimensions of each of the FrameLayouts and call the layout method for each
#Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
{
if (changed)
{
float width = (float)(right - left) / (float)this.digits;
if ((int)(width/ASPECT_RATIO) > bottom- top)
width = (float)(bottom - top) * ASPECT_RATIO;
this.digitWidth = (int)width;
this.digitHeight = (int)(width/ASPECT_RATIO);
this.xOffset = ((right - left) - (this.digits * this.digitWidth))/2;
this.yOffset = ((bottom - top) - this.digitHeight)/2;
for (int i = this.digits-1; i >=0; i--)
{
int x = xOffset + i * this.digitWidth;
this.placeFrames.get(i).layout(x, this.yOffset, x + this.digitWidth, yOffset + this.digitHeight);
}
}
}
The issue I am having is that the FrameLayouts are being correctly sized and positioned within the custom GroupView, but the ImageViews are not being resized by the FrameLayouts - they have zero width and height. I validated this using the HierarchyViewer.
I think I've missed something - I assumed that the layout method on the FrameLayout would take care of resizing the child ImageViews based on the layout parameters passed in when they were added to the FrameLayout as children, but it seems not.
A pointer in the right direction would be very much appreciated.
Thanks
Andrew
I assumed that the layout method on the FrameLayout would take care of resizing the child ImageViews based on the layout parameters passed in when they were added to the FrameLayout as children, but it seems not.
You are not layout() these ImageView objects yourself nor calling super.onLayout()..
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).