I'm coming from iOS where I can simply reparent a view under another view. I'm trying to figure out the equiv in Android. I want to move an imageview up a couple of spots in the view hierarchy when clicked. Something like:
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
//Tile touchBegan
if(action == MotionEvent.ACTION_DOWN) {
RelativeLayout rl = (RelativeLayout)(getParent().getParent());
rl.addView(this);
}
}
This is a simplified example of course.. but just trying to figure out how to re-parent a view under another layout. I found this example searching around which seems incredibly complicated..
http://blahti.wordpress.com/2011/02/10/moving-views-part-3/
If I remember my old iOS experience right it removes the view from it's old parent automatically if you add it to another one (I might be wrong ;-).
Here's how you would do the same on android:
ViewGroup parent = (ViewGroup) yourChildView.getParent();
if (parent != null) {
// detach the child from parent or you get an exception if you try
// to add it to another one
parent.removeView(yourChildView);
}
// you might have to update the layout parameters on your child
// for example if your child was attached to a RelativeLayout before
// and you add it to a LinearLayout now
// this seems very odd coming from iOS but iOS doesn't support layout
// management, so...
LinearLayout.LayoutParams params = new LinearLayout.LayoutP...
yourChildView.setLayoutParams(params);
yourNewParent.addView(yourChildView);
((ViewGroup)view.getParent()).removeView(view);
newContainer.addView(view)
Related
I have two views A and B. View B is completely covered view A which means the bounding rect of view B is more than bounding rect of view A.
The best example I can give is, I have a fixed bottom tabs layout and a RecyclerView with multiple linear layouts. When I scroll, one of the linear layout will be behind the bottom tabs layout.
So, How can I programatically check If view A is completely covered by other views (other views might not be known upfront)?
Is there any possible way to identify this?
Update:
I have tried the solution mentioned here. But it didn't solve my problem. In my case, the view bounds are exactly matching and I want which view is on top.
After lot of struggle, I have found a way to identify if view is overlapped by other views in the following way.
AccessibilityNodeInfo nodeInfo = AccessibilityNodeInfo.obtain();
viewA.onInitializeAccessibilityNodeInfo(nodeInfo);
if (!nodeInfo.isVisibleToUser()) {
// View is not visible to user. This also validates if viewA is overlapped by other views
}
nodeInfo.recycle();
Use the following method it will help you to find view overlapping:-
private boolean isYourViewOverlapping(View firstView, View secondView) {
int[] firstPosition = new int[2];
int[] secondPosition = new int[2];
firstView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
firstView.getLocationOnScreen(firstPosition);
secondView.getLocationOnScreen(secondPosition);
int r = firstView.getMeasuredWidth() + firstPosition[0];
int l = secondPosition[0];
return r >= l && (r != 0 && l != 0);
}
I am developing my first Android App and after a good start, I have spent days of deep debugging on a problem, which by now seems to be an error in the implementation of View.requestRectangleOnScreen in API-23 and probably many levels before that. Just now, I have discovered that the implementation of this routine is changed significantly in API-25.
The problem is that a request for focus on an EditText placed inside a HorizontalScrollView may cause the HorizontalScrollView to scroll away from the field requesting the focus.
In my case it is an EditText with centered text, which is then placed in the center of 1048576 pixels and scrolled roughly half a million pixels to the right making the text centered and visible (this part is perfectly ok!) But then this offset of half a million pixels is propagated up the parent chain and makes the HorizontalScrollView move to its far right and far away from the input field.
I have tracked it down to the View.requestRectangleOnScreen routine, which in the API-23 sources is as follows:
public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
if (mParent == null) {
return false;
}
View child = this;
RectF position = (mAttachInfo != null) ? mAttachInfo.mTmpTransformRect : new RectF();
position.set(rectangle);
ViewParent parent = mParent;
boolean scrolled = false;
while (parent != null) {
rectangle.set((int) position.left, (int) position.top,
(int) position.right, (int) position.bottom);
scrolled |= parent.requestChildRectangleOnScreen(child,
rectangle, immediate);
if (!child.hasIdentityMatrix()) {
child.getMatrix().mapRect(position);
}
position.offset(child.mLeft, child.mTop);
if (!(parent instanceof View)) {
break;
}
View parentView = (View) parent;
position.offset(-parentView.getScrollX(), -parentView.getScrollY());
child = parentView;
parent = child.getParent();
}
return scrolled;
}
The idea is to make the rectangle visible by scrolling it onto the screen in every containing View, starting at the leaf level and passing the request up the chain of parents. The initial rectangle is given in child coordinates, which of course have to be adjusted as we work our way up the chain of parents. This is done with the statement
position.offset(-parentView.getScrollX(), -parentView.getScrollY());
close to the end of the code above.
What I have found, is that this is wrong because we are transforming the position given in child coordinates using the scroll X/Y values pertaining to the parent coordinates. Using the scroll X/Y of the child instead solved my problem but it was not possible to make a perfect override of this routine because it relies on private member variables. Specifically, I found no way of mimicing the mAttachInfo.
Now, digging a bit further, I found that the code for this routine in API-25 has changed significantly and (IMHO) correctly to the following:
public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
if (mParent == null) {
return false;
}
View child = this;
RectF position = (mAttachInfo != null) ? mAttachInfo.mTmpTransformRect : new RectF();
position.set(rectangle);
ViewParent parent = mParent;
boolean scrolled = false;
while (parent != null) {
rectangle.set((int) position.left, (int) position.top,
(int) position.right, (int) position.bottom);
scrolled |= parent.requestChildRectangleOnScreen(child, rectangle, immediate);
if (!(parent instanceof View)) {
break;
}
// move it from child's content coordinate space to parent's content coordinate space
position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY());
child = (View) parent;
parent = child.getParent();
}
return scrolled;
}
The most important change being the line
position.offset(child.mLeft - child.getScrollX(), child.mTop -child.getScrollY());
where the scroll X/Y adjustment is now made with child values.
Now, I have two questions.
First, do you agree with my observations above?
Second, how do I implement an App that can be used on both API-23 and API-25 under the given circumstances?
My current thoughts are to sub class the EditText and override the requestRectangleOnScreen method such that when the API is 25 and above, it just calls the super class method and when the API is below 25, I basically do a full override using code along the lines of the code from API-25 but then missing out on the mAttachInfo part.
I am trying to add a View to a RelativeLayout in my OnClickListener.
montrolButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// myParent is a relative layout
// newChild is an ImageView
myParent.addView(newChild);
requestLayout();
}
});
I have looked at the HierarchyViewer, I don' see my new child being added.
Can you please tell me if I miss anything?
I just tried the very same code and it works as it should. There could be an issue with your variable myParent which is not the element you expect it to be.
Also I did not have to call requestLayout() for the added view to appear on the screen.
Possibly try to just explicitly get another part of your view and add it there to see what is happening. Also just to try, you may do this:
RelativeLayout rv = (RelativeLayout) this.findViewById(R.id.right3);
ProgressBar iv = new ProgressBar(this);
rv.addView(iv);
to see if there is anything wrong with your image view instead of myParent
In any case it works if both elements are OK - there is nothing else to do in an activity.
Do you set the layout attributes of the new view (image view)?
I have a fullscreen view flipper on the galaxy tab containing two relative layouts, each of which is displaying between five and ten image views.
I am having problems when I try to slide one of the view flipper viewso ut and the other in, the "animation" does nothing and then just switches.
If I remove all but one of the image views in each of the view flipper children the animation is fine.
Can I somehow force it to use a mixed down version of the image for the animation, or is the relative layout container causing the performance problem?
There should be nothing going on at the time the flipper animation occurs so I can't understand the problem.
Thanks
EDIT: Some of the problematic code: Perhaps this might indicate areas causing the performance problems?
I have in my onClick, two method calls, used for switching backwards through the views and forward. A view is "generated" immediately before being added to the flipper and switched.
so, on the click of the back button (for example) I have (amongst other things) ....
nextView = new RelativeLayout(this);
makeAView(selectedViewObjects, viewHolder2);
switchViewNow(-1);
currentView = nextView;
...
private void makeAView(ViewObjects vObjects, RelativeLayout nextView)
{
for(Object obj : vObjects.viewObjects)
{
if(obj instanceof Image)
{
addImageView(nextView, obj);
{
else
{
if(obj instanceof Anim)
{
addAnimView(nextView, obj);
}
}
Where Image is a custom View implementation for displaying a static image and Anim is a custom view implementation for displaying an AnimationDrawable (multiframe image cycle).
Next we have the switch method:
private void switchViewNow(int direction)
{
if(direction == -1)
{
viewFrame.setInAnimation(AnimClass.inFromLeftAnimation(null));
viewFrame.setOutAnimation(AnimClass.outToRightAnimation(null));
viewFrame.showNext();
}
else
if(direction == 1)
{
viewFrame.setInAnimation(AnimClass.inFromRightAnimation(null));
viewFrame.setOutAnimation(AnimClass.outToLeftAnimation(null));
viewFrame.showNext();
}
}
I could be wrong, but it looks to me like you have overloaded nextView in a confusing way.
It is defined outside of makeAView, but makeAView expects a parameter which it names nextView.
You are passing viewHolder2 as nextView to this function.
This means that makeAView will NOT change the 'global' nextView, it will change viewHolder2.
But then you seem to assume that the 'global' nextView has been modified because you use it to assign currentView.
I think it would be a good place to start if you assign unique names to these two versions of nextView.
I wonder if you are even still looking for a solution: this post is rather old and ignored.
I am trying to use a ViewFlipper and make it act like the home screen(The layout will move with your finger). Check out this for an example. I want to do this with a ViewFlipper that only contains two children so the opposite view should be shown on either side of the current view depending on which way the user moves their finger. This code works but only for 1 direction at a time. This is in onTouchEvent.
case MotionEvent.ACTION_MOVE:
leftView.setVisibility(View.VISIBLE);
rightView.setVisibility(View.VISIBLE);
// move the current view to the left or right.
currentView.layout((int) (touchEvent.getX() - oldTouchValue),
currentView.getTop(),
(int) (touchEvent.getX() - oldTouchValue) + 320,
currentView.getBottom());
// place this view just left of the currentView
leftView.layout(currentView.getLeft() - 320, leftView.getTop(),
currentView.getLeft(), leftView.getBottom());
// place this view just right of the currentView
rightView.layout(currentView.getRight(), rightView.getTop(),
currentView.getRight() + 320, rightView.getBottom());
Which ever of the bottom two lines I put last that direction will work correctly but the other will not.
Here is how I set the leftView and rightView:
final View currentView = myFlipper.getCurrentView();
final View leftView, rightView;
if (currentView == meView) {
Log.d("current layout: ", "me");
leftView = youView;
rightView = youView;
} else if (currentView == youView) {
Log.d("current layout: ", "you");
leftView = meView;
rightView = meView;
} else {
leftView = null;
rightView = null;
}
Is it going to be possible to set it up so that the same view is shown on both sides of the current view?
Thanks stealthcopter
That worked here is the new code if anyone is interested.
if (touchEvent.getX() < oldTouchValue){
// place this view just right of the currentView
rightView.layout(currentView.getRight(), rightView.getTop(),
currentView.getRight() + 320, rightView.getBottom());
}else if (touchEvent.getX() > oldTouchValue) {
// place this view just left of the currentView
leftView.layout(currentView.getLeft() - 320, leftView.getTop(),
currentView.getLeft(), leftView.getBottom());
}
I also moved the setVisibility() calls to the MotionEvent.ACTION_DOWN in an attempt to get rid of some flickering of the views. This helped but I still get a bit.
I have possibly not very constructive suggestion, but if you want it to behave like a home screen, why you don't want to look at the src of that, and modify it to your needs ?
To get rid of the flickering, set setVisibility(View.INVISIBLE) in MotionEvent.ACTION_DOWN. The default value is "GONE" on a view. That means that you can not set Layout() on a view until it's either "VISIBLE" or "INVISIBLE". So in three steps:
Set Visibility to INVISIBLE on the View
Set Layout() on the View
Set Visibility to Visible on the View
If I understand your request, this effect should now be implemented using a View Pager
Looks like this: