I have a custom view which is nested inside of a ScrollView. The idea is that the custom view can be longer than the height of the screen, so when you swipe through the ScrollView you can see all of its contents.
In order to do this I adjust the clip of the custom view's canvas like this:
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.clipRect(mCanvasArea, Region.Op.REPLACE);
}
Where mCanvasArea is a pre-generated RectF which represents the entire area the canvas can draw. I logged it's value # 10308.
However when the app runs, the custom view is unscrollable, it acts as if the visible content represents all of the content in the canvas, but I can see more peeking up from the bottom of the screen.
Here is my XML declaration for the view:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/activity_toolbar"
android:fillViewport="true">
<com.myapp.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
(I had to add fillViewport in order to get the content to show up in the first place)
Is there anything else that needs to be done in order yo make the view scroll?
You will need to specify the height of the custom view, not the canvas. The canvas passed to onDraw reflects the width and height of the view.
Setting the view to wrap_content doesn't mean it will grow whenever you draw outside of its bounds. It will only "wrap" the child views, not the canvas.
To set your view's height programmatically:
getLayoutParams().height = 500;
invalidate();
I'm not sure what you are going for with this exactly so I can't get too much more specific about where you should set the height, but I'd recommend not changing your view's dimensions in onDraw because when you invalidate it will cause another draw and you'll need some funky logic to handle this recursion.
Instead you could determine the height you need when the view is constructed, and then override onMeasure with something like this:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, calculatedHeight);
}
This would force your custom view into the calculated height.
Related
I have a Framelayout, where the first element is an ImageView which height and width is match parent and let the id is A. Second element is also an ImageView which height and width also match parent and let the id is B. Third element is a View which height and width is 100 dp and can be move to the whole screen and let the id is C. I am using transparent color in background C, so inside C we should see B, because B is above A. But I want to show A in inside C , how can I do that?
If I understand your question correctly, you basically want to have the intersection of View C and View B transparent to see View A which is behind both of them.
In that case it might be enough to get visible rectangle of view C:
Rect rect = new Rect();
viewC.getGlobalVisibleRect(rect);
And then you can draw this rectangle as transparent mask on view B, which will therefore allow you to see View A that is behind view B. You can achieve it by overriding onDraw method of view B.
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas)
canvas.drawRect(rect, paint);
}
This will achieve something similar to this:
You should also not forget to disable HW acceleration for view on which you are drawing (View B in this case) and also tell the view that you are going to draw to it manually:
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, null)
Further explanation and example can be found here:
Android canvas: draw transparent circle on image
You can set visibility of B to invisible or gone, if you don't want to show B. Or you can replace A with B. As frame layout work like stack first thing you put goes to the bottom. so you put in this format B->A->->C
use relative layout to wrap your framelayout then you can use layout above and then work with visibility of views
This will help.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/a"
android:src="#drawable/ic_networking"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/b"
android:visibility="gone"
android:src="#drawable/dailymetricsin"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<View
android:background="#90606060"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
You can hide and show the ImageView based on the which one should show in the background either A (or) B.
I have a round corners layout, now I want to add a child view (an Imageview) which matches the parent layout's height and width.
My problem is that the child view hides the round corners of the parent.
How can I constrain it inside the borders of the parent layout without using the margin property, so that the parent's round corners stay visible?
PS: I created round corners of parent layout by overriding the onDraw() method.
My code:
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, width, height, mpaint);
super.onDraw(canvas);
}
In my opinion, you can put your child views into a CardView(in support v7), which is actually a FrameLayout, but it handle the corners by just set one line of code:
app:cardCornerRadius="3dp"
It can clip the corner with the radius you set no matter what the child views are.
I suggest you add padding to the "rounded corner" view. This could be padding on all sides, bottom and top or left and right. Depending on what suits you the best.
I can't think of a more simple method than this. Your onDraw method looks fine, first the background than the child views.
When you add a child to your ViewGroup, that child is being drawn on top of your ViewGroup, thus your rounded corner doesn't take effect.
In order to achieve your goal you have to perform clipping on a certain path in your layout. That sounds a bit complicated, but in fact it is not.
Essentially, you can understand clipping as "cutting off" some part from your layout.
RectF rect = new RectF(0, 0, width, height);
path = new Path();
path.addRoundRect(rect, cornerRadius, cornerRadius, Direction.CW);
canvas.clipPath(path); // clipping here
// now anything that is outside this path will be "clipped", i.e. not drawn
You can refer to this for a complete source code.
Is there a way to dynamically change the position(like a manually translation animation) on a child view thats been defined and places inside onLayout?
i have this onLayout defined here:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//place topview at the top of the layout by removing the ySpace value defined fro the height
// of this view and dividing the height so that the topView takes nearly half the space
// height of this view container
topView.layout(0, 0, getWidth(), (getHeight() - ySpace) / 2);
}
i want to manually change the y axis position of topView Dynamically .
i was thinking ofabout onDraw but onDraw doesnt that childViews and draws them. Instead it just gives you a blank canvas to manually draw items in it.
Currently implementing a custom ViewGroup that takes x amount of child views and dynamically moves them around. My question is how do i move them around?
I'm trying to display values that a user enters on an image, there are 4 versions of image (ldpi, mdpi, hdpi and xhdpi).
I want to position the textViews on a "fixed" location that adjusts to the right screensize.
Has anyone got some advice on how I could achieve this?
Here is a sketch of what I'm trying to do.
http://dl.dropbox.com/u/251053/stackoverflow.png
edit
Thanks for the fast responses.
Uploaded a new image, this isn't possible with relative layout I guess?
http://dl.dropbox.com/u/251053/stackoverflow1.png
Look at the "RelativeLayout" documentation: http://developer.android.com/reference/android/widget/RelativeLayout.html
You will be able to put your textView around your ImageView.
The best way to solve this I think is to create a custom layout that expects the ImageView as its first child.
All the other children will be the TextView's that you can either:
position relatively to the position of your ImageView
position proportionally to the height/width of your parent custom layout that matches the height/width of your ImageView
The left/top position information of the child TextView's can be given through a custom LayoutParams class that suits your needs.
I think the best way is to create a custom view by extending View class. It is not so complicated as it may look. Here you can find more info about custom UI components.
The main idea is to override onDraw(Canvas canvas) and onMeasure(int widthMeasureSpec, int heightMeasureSpec) methods.
In onMeasure you should return the dimensions of your view and also you may calculate the size and angle of the rectangle and also the size of text you want to draw. Or you can implement custom resize() method that will be called explicitly on you view from the activity and make text and image calculations.
When you know all the measurements you can position and draw both image and text in onDraw(Canvas canvas) method.
#Override
protected void onDraw(Canvas canvas) {
myImageDrawable.setBounds(0,0,50,50);
myImageDrawable.draw(canvas);
canvas.drawText(myText, 10, 10, myTextPaint);
}
You can find more info about Canvas class here.
Also you may use StaticLayout to measure text.
I have a custom view that is sort of a vertical progress bar. It's just a custom view, drawing onto the canvas to fill from bottom to top with the percent complete. In my xml, I have text above, and text below the custom control (there are two of these, one on the left, one on the right, here's the right):
<RelativeLayout android:layout_width="150dp"
android:layout_height="fill_parent" android:layout_alignParentRight="true">
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/bar_right_text" android:text="Right"
android:layout_alignParentTop="true" android:gravity="center_horizontal" />
<com.myApp.VBarView android:id="#+id/right_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/bar_right_text"/>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/bar_right_time_text" android:text="Time Remaining" android:layout_below="#id/right_bar" android:gravity="center_horizontal" />
</RelativeLayout>
In my custom view, I check the height in every way I can think of, but I always get a height that goes all the way to the bottom of the screen
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// h here is 489
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
int myHt = getHeight();
int myMeasHt = getMeasuredHeight();
Rect canvasRct = canvas.getClipBounds();
// all of these (myHt, myMeasHt, and canvasRct.getHeight()) are all 489
}
So far, I have tried reversing the views in the layout, so the top text is first (top aligned), the bottom text is second (bottom aligned), and the custom control is last (layout_above the bottom text), with both match_parent and wrap_content, to no avail. I tried changing to a linear layout, still with no better result.
In the case shown in code, the top text is displayed, but the bottom text is gone. When I changed the order so the custom view was layout_above the bottom text, it just moved the drawing area up so the bottom text was visible, but covered the top text. Note that when I say the bottom text is gone, I mean gone, not just covered up. My final test was to hardcode the layout_height to "350dp" in the layout.
In all cases, I always get a height of 489 on this particular device, even with the height hard-coded. I can not figure out what I need to do to get the middle part of the screen as something I can draw on and leave the rest alone. Are custom views assumed to be the last thing in a layout? The only way I can get this to work is to change the order so the custom view is last in the layout so that the bottom of the custom area is on top of the bottom text, the bottom text is still displayed, and then manually add about 150 to the top (i.e. don't write into zero on the canvas provided to the onDraw()) so it doesn't cover the top text. Fortunately, the graphic I'm drawing in is semi-transparent, so I can see whether the text is covered or just gone.
Does anyone have more info on how this is supposed to be working? Did I manage to make the explanation of what I did more confusing than the actual problem?
Thanks,
gb
Someone suggested offline that instead of just placing the custom field "above" or "below" one of the other fields, do both. Keep it as the last item in the XML, and set android:layout_above and android:layout_below. I did that, and now my heights come in correctly.