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:
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.
In scrollview, if I add any view in middle, normally all the views below the added view scrolls downside. But I want to scroll the top views of added view to upside without disturbing the bottom views. Is it possible in scrollview , please help me ?
In the figure , If view 4 was added , then view 1 has to be scrolled upwards , without changing the positions of view 2 and view 3.
You can probably get the height of the view you are adding with and then scroll the scrollview manually that many pixels
scrollView.scrollBy(0, viewAdded.getHeight())
I've been wanting to try this question for quite some time, I finally got the chance today. The method is pretty simple (in fact, #dweebo already mentioned it earlier) - we move the ScrollView up as we add the view. For getting precise (and valid) dimensions when adding, we use a ViewTreeObserver. Here's the code you can get hints from:
// Getting reference to ScrollView
final ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView);
// Assuming a LinearLayout container within ScrollView
final LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
// The child we are adding
final View view = new View(ScaleActivity.this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
view.setLayoutParams(params);
// Finally, adding the child
parent.addView(view, 2); // at index 2
// This is what we need for the dimensions when adding
ViewTreeObserver viewTreeObserver = parent.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parent.getViewTreeObserver().removeOnPreDrawListener(this);
scrollView.scrollBy(0, view.getHeight());
// For smooth scrolling, run below line instead
// scrollView.smoothScrollBy(0, view.getHeight())
return false;
}
});
I have framelayout which contains two relative layouts, one is on top of the other. When user clicks a button, the one on the top move 80% off the screen to the right. Then one on the bottom becomes clickable. This is what it looks like.
FrameLayout
RelativeLayout (bottom) RelativeLayout (top)
FilterWidgets Open/close button, ListView
It's really easy to achieve on 3.0+ with the new animation api which is Property base Animation. For the pre 3.0, because animation is view based. So I end up manually modify the layout property on onAnimationEnd. The call requestLayout to make it permanent, but only to find out the layout reverts back to original position. Anybody know how to move layout permanently?
see my other post if you want to see the whole picture:
https://stackoverflow.com/questions/14541265/changecursor-cause-layout-container-of-the-listview-to-reposition
theTranslationX.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator nullPointer) {
v.clearAnimation();
int theL = isMenuOn() ? 0 : v.getLeft() + getFilterWidth();
int theR = isMenuOn() ? v.getWidth() : v.getLeft() + getFilterWidth() + v.getWidth();
int theHeight = v.getHeight();
int theT = 0;
v.layout(theL, theT, theR, theHeight);
v.requestLayout();
}
});
This is 9 months late but try using:
yourView.layout(left,top,right,bottom); //all parameters are type int
However I don't think this is permanent, the position of the view will still be reset when you call requestLayout(), but give it a try.
I have a HorizontalScrollView with a LinearLayout inside.
This LinearLayout is populated (dynamically) with a lot of TextViews.
I want to scroll my HorizontalScrollView to a specified TextView depending on some index.
I try this but doesn't work
while (!this.stopsCursor.isAfterLast()) {
int index = this.stopsCursor.getInt(this.stopsCursor.getColumnIndex("index"));
if (index == session.getServiceStopIndex()) {
TextView view = (TextView) this.linearLayout.getChildAt(index);
StyleHelper.setStopHighlight(view);
Log.v("TEST", "Left : " + view.getLeft() + " Right : " + view.getRight());
int offsetX = ((view.getLeft() + view.getRight()) / 2);
this.horizontalScrollView.scrollTo(offsetX, 0);
}
this.stopsCursor.moveToNext();
}
My logger show me Left : 0 Right : 0
Can someone help me?
Thanks in advance!
The problem is probably that you are attempting to get the left and right of the view before layout. You need to move this code later in the layout flow. It's hard to say exactly where to move it without more context as to when you want to scroll, but if it's immediately after creation (and I assume that you're populating the LinearLayout somewhere around the Activity's onCreate) then you might want to look at overriding View.onSizeChanged and doing it around then.