I have a ViewGroup and I'm adding child Views to it like this:
mViewGroup.addView(view, new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
(the WRAP_CONTENT is important for the onclicklistener to work as expected).
So far so good, but later I move the child Views around dynamically by either overriding their onDraw methods or by overriding the ViewGroups:
protected boolean getChildStaticTransformation(View child, Transformation t)
The problem is that the Children disappear if they are moved outside their original region which seems to be calculated when they are first added. I tried calling setClipChildren(false); but it didn't work.
I could solve the problem by using LayoutParams.FILL_PARENT when adding the child views but in this case the onClickListener would react to any click on the whole ViewGroup and I want it to only react to clicks on the specific area where my transformed child View is located.
Maybe I could still use LayoutParams.FILL_PARENT if there is a way to define where the clickable region for the View should be.
Any suggestions?
Perhaps you should experiment with margins and paddings of your child views. Otherwise you could try to use a different ViewGroup layout like a FrameLayout where the postion of each view can be set by its margins.
Related
I have problems doing a small game which adds a lot of views in a ConstraintLayout (I already tested with FrameLayout, RelativeLayout and LinearLayout. First two with same result and Linear with very rare behaviour) and changes the size and position of the views.
Each game loop (33ms) some of the views are changing it's size and position, so I do this on the LayoutParams variable applied on the View on each loop with the new size.
params.width = realWidth;
params.height = realHeight;
and I do this for the position:
view.setX(realX);
view.setY(realY);
The problem is that the change is not reflected if I didn't call view.requestLayout() and that is a huge problem because requestLayout() is repaiting the parent layout and all its childrens, slowing down the game.
How can the size changes of a view be reflected without calling requestLayout? I read that you can achieve that with view.layout(). So I changed my code with this:
view.layout((int)realX, (int)realY, (int)realX+realWidth, (int)realY+realHeight);
The problem is that it works without calling requestLayout() but the view has a very rare behaviour using layout(), and when I add more views, the views become some milliseconds invisible and appear on the left top corner, after that they appear in the correct position, but suddenly they become invisible again and again and again etc in a very rare behaviour loop.
ConstraintLayout is slow, particularly if it contains many Views and much slower if it contains nested ConstraintLayouts.
Because of you are manually setting the position for your Views, you can use a different ViewGroup than ConstraintLayouts and set the absolute position with setX(), setY(), setTop(), etc.
I have a created a mapview with markers on it.
Looking at this picture below:
Grandparent is a filling View
Parent is my MarkerView
Child is a marker which is clickable
Parent has clipChildren(false) and thus the children are visible.
My problem is that the children are clickable, except for the part where Child 2 is outside the Parent.
Parent also has the appropriate TouchDelegate (and I also tried this for the children).
How can I make the complete child clickable?
I couldn't make it work without changing the elements.
I ended up enlarging the parent and using setTranslationY for the markers to keep them in place like this:
I had a similar issue and fixed it by setting app:elevation="XXdp" to the child.
The reason why you can't do this is that the default implementation of ViewGroup#dispatchTouchEvent(MotionEvent ev) iterates over the children (not the grandchildren) to look for a child that are a target (is on bottom of the click event point) and can receive touch events. It will find nothing if you touched the part that it is outside the bounds of Parent, which is the only child of Grandparent. If it does not find Parent, Parent will never be able to make a look up on its children (Children 1 and Children 2) and eventually dispatch the event to Child 2.
So you either increase the size of Parent (as the acceptable answer), which is the most easy way, or you will have to override the method ViewGroup#dispatchTouchEvent(MotionEvent ev) of your Grandparent to make a more complex lookup, like looking up grandchildren too. The method is already fairly complex, if anyone finds a implementation, please share because I don't have one.
THE CONTEXT
I'm trying to create a custom view that extends the LinearLayout. In short, this view draws its own background by overriding View.onDraw() method. The background has a shadow around it (much like the CardView).
THE OBJECTIVE
Since the background is like the card view with a shadow, child views should not be allowed to fill in the entire view. Fixed padding should be left for the shadows around the card. This padding should not be effected the padding set by the xml padding attribute or setPadding() methods. Its something akin to the CardView's setContentPadding() method.
THE APPROACH
I'm trying the achieve this by overriding the onLayout() method of the LinearLayout. The official documentation says:
Called from layout when this view should assign a size and position to each of its children. Derived classes with children should override this method and call layout on each of their children.
The LinearLayout's implementation of the onLayout() works well work my case. So don't want to rewrite the the entire implementation. Thus I tried to achieve this by overriding the onLayout() method in my custom class and changing the arguments passed to super to account for the shadow as shown below:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(
changed,
left + shadowSize,
top + shadowSize,
right - shadowSize,
bottom - shadowSize
);
}
THE PROBLEM
The problem, however, is that this has no effect on the LinearLayout's children. No matter what values I pass to super, the child views remain the same.
THE QUESTION
Why is this not working? Or rather whats the best approach to achieve a content inset/padding for classes such as the LinearLayout that already have an onLayout() implementation that I don't want to modify or rewrite?
It's not working because width and height are attributes which provide information for the parent of the View. Anything that starts with layout_ is an instruction to the parent.
In your case, onLayout you are supposed to layout the children of the ViewGroup, not change the dimensions of the current view. In onMeasure is where you measure the children. And onLayout is where you position them. You CAN indent all the children if you want in onLayout but you can't do it if you're calling the parent.
This post may help understand build custom ViewGroup
What I recommend is that you extend ViewGroup and have only one child in the custom ViewGroup, then layout the child to the same size as your custom view group while adjusting for the shadow. This will allow you to set a shadow on your custom ViewGroup while avoiding the issue of drawing outside the bounds. This is the easiest approach. you could layout each child if you want. Also, if using the new ConstraintsLayout is an option, that may help prevent having to use a custom ViewGroup.
I'm working on a custom ViewGroup.
This ViewGroup has a bunch of children. I need to animate a few and change their position. I understand that Android animations move just the bitmap and not the real object. I've been trying to MOVE them by following various resources but have failed.
What I'm doing with ViewGroup so far:
Measure children and the ViewGroup
Position children in onLayout
What I'm trying to do further
Use a custom animation to move a small subset of the children. I'm using a custom Animation object because I need to move a bunch of Views and I'm applying translationX on all of them together. The other option that I know is to start a separate Animation on all of them and the thought of which makes me think it's gonna be unoptimized.
Problem
Views animate fine, but their real position remains unchanged. So the next time I'm trying to do the same kind of animation, but on the new co-ordinates, it doesn't work. Because, their positions haven't updated.
What did I try
Use onAnimationEnd to layout each of the children to the new left, top, right and bottom position. All views vanished
On onAnimationEnd, reset translationX to zero and then start re-positioning the views. No effect of calling view.setTranslationX(0f)
Can someone please help me with the correct way of doing this? Thanks
when animating call layout() on your child Views
Let me explain the scenario that I want to achieve:-
Consider the below as the Layout I have inside a Parent_Linearlayout:
[Linear Layout] (Fill_Parent, Wrap_Content)
[ScrollView]
Activity's setContentView is set to the Parent_Linearlayout
In the application, when a condition is met, I want the Scrollview to be removed from the screen and instead put another View in its place.
I've been able to do this, & when I remove the ScrollView, I'm applying translate Animation to it so that it seems as if the View has gone to the top -before removing it.
But when the animation occurs, the ScrollView translates OVER the Linear layout present above it.
How do I restrict it, so that the scrollview does not go over the linear layout, but disappears at the base of the Linearlayout. I want the linearlayout to always stay visible..
I've been trying to do this from quite some time, but I've not been able to get desired results..
Could someone kindly help me out here??
I don't quite understand your description of your layout, but the Android view system is drawn based on the ordering of the views in the hierarchy. Views added later to a parent are drawn after those added earlier. So if you always want the LinearLayout to be drawn on top of the ScrollView if/when they overlap, then declare or add the ScrollView object to its parent before the LinearLayout object.
In thinking more about this, I suppose the ordering here is important because you want the ScrollView to be placed below the LinearLayout in the parent of both of these views. Putting the ScrollView first (and thus having it painted first) would then put it above the other LinearLayout, which isn't what you want.
There are various ways to achieve what you want. For example, you could use a RelativeLayout as the parent of the views, then the ordering is not important.
Alternatively, you could place the ScrollView inside another LinearLayout (and that LinearLayout would be the second child of the overall parent layout). Then when you animate the ScrollView, it would be clipped by its immediate parent, which I believe would give you the effect you're looking for (make sure that setClipChildren() is set to true on this new intermediate LinearLayout, which it is by default, otherwise it won't clip the ScrollView as it animates out of it). Note that this approach would necessitate different animation values, since you are now animating the view outside of its parent (the new LinearLayout).