Why setY in a Framelayout not work? - android

I put an Imageview in a match_parent Framelayout, the Framelayout's visibility is GONE, then caculate the ImageView's coordinate and setX/Y then make the Framelayout VISIABLE. But the ImageView's layout position is weird!
The Code is simple:
override fun onVisibilityChanged(
changedView: View?,
visibility: Int) {
if(visibility == View.VISIBLE && mTargetView != null){
var location = intArrayOf(0,0)
mTargetView?.getLocationOnScreen(location)
var x = location[0] + mTargetView?.width!! / 2 - mIconWidth/2
var y = location[1] - mIconHeight / 2 - mStatusBarH
Log.d("lee","\nbefore x: ${mDelView.x}, y: ${mDelView.y}")
/*
* use this code will get the right result.
mLayoutParams.leftMargin = x
mLayoutParams.topMargin = y
*/
mDelView.x = x.toFloat()
mDelView.y = y.toFloat()
Log.d("lee","\nafter x: ${mDelView.x}, y: ${mDelView.y}")
}
super.onVisibilityChanged(changedView, visibility)
}
The log show the coordinate is correct:
lee: before x: 492.0, y: 332.0
after x: 492.0, y: 332.0
The layout position show the ImageView's layout position always at the left top corner. When use the picture bellow is using the LayoutParams margin and the layout position is right. It really confused me.

The layout position show the ImageView's layout position always at the left top corner
Because this is how FrameLayout works. You want to position container's childs by hand, use either RelativeLayout or write own container.

The View's layout bounds decided by the view's left, top, bottom, right position, relative to parent. The second phase of the layout mechanism will caculate these position.
/**
* Sets the visual x position of this view, in pixels. This is equivalent to setting the
* {#link #setTranslationX(float) translationX} property to be the difference between
* the x value passed in and the current {#link #getLeft() left} property.
*
* #param x The visual x position of this view, in pixels.
*/
public void setX(float x) {
setTranslationX(x - mLeft);
}
The setX source code show this function only change the the visual x position of this view, and the mLeft value not change. So setX/Y will not change the layout positon.

Related

Viewpager page transformer caoursel issue

Haii developer, i have issue to implement view pager transformer like design below, in design its not try to scale the page, the page is still have same size, but the highlight item(in center) have y axis higher then other. This is my expected view result:
But this how i get with my current code:
Here is my code for transforming page:
val nextItemVisiblePx = resources.getDimension(R.dimen.viewpager_next_item_visible)
val currentItemHorizontalMarginPx =
resources.getDimension(R.dimen.viewpager_current_item_horizontal_margin)
val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
page.translationX = -pageTranslationX * position
// Next line scales the item's height. You can remove it if you don't want this effect
page.scaleY = 1 - (0.25f * kotlin.math.abs(position))
// If you want a fading effect uncomment the next line:
page.alpha = 0.6f + (1 - kotlin.math.abs(position))
}
layoutBaseFollowingFeedsSuggestion.rvSuggestionFollowingFeedList.setPageTransformer(pageTransformer)
// The ItemDecoration gives the current (centered) item horizontal margin so that
// it doesn't occupy the whole screen width. Without it the items overlap
val itemDecoration = HorizontalMarginItemDecoration(
requireActivity(),
R.dimen.dimen_25dp
)
layoutBaseFollowingFeedsSuggestion.rvSuggestionFollowingFeedList.addItemDecoration(itemDecoration)
Thanks!

Problematic clip already set on Canvas in onDraw

I subclassed TextView to provide a custom onDraw. But canvas has a clip region applied that is nonsensical: the x is set to something well outside the view bounds. I think that's thwarting my purposes.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// draw numberLabel
if (numberLabel == 0)
return
val right = this.width - this.resources.getDimension(R.dimen.topNavBadgeEndMargin)
// top needs to add the top margin and estimated text height
val top = this.resources.getDimension(R.dimen.topNavBadgeTopMargin) + this.badgePaint.textSize
canvas.drawText(numberLabel.toString(), right, top, this.badgePaint)
val r = Rect()
canvas.getClipBounds(r)
Log.d("TopNav", "canvas.clipBounds: $r")
}
Logcat printed:
D/TopNav: canvas.clipBounds: Rect(524187, 0 - 524389, 147)
FYI, I have also tried drawing a circle r=50 center=(100,100) and it doesn't show. So what would help is a) why this happens? b) I know there's no way to reset the clip region, but is there any workaround that would help me?
Seems like if you override onDraw in a TextView you need to offset by scrollX (probably should do scrollY as well, though it was zero). scrollX was the rediculously large int and I have no idea why it would be nonzero in a TextView that doesn't need to scroll.
val right = this.scrollX + this.width - this.resources.getDimension(R.dimen.topNavBadgeEndMargin)
If you have several operations then canvas.translate wrapped by save and restore probably helps.

What is the android equivalent of UIView's convertRect / convertPoint functions?

UIView has the following:
- convertPoint:toView:
- convertPoint:fromView:
- convertRect:toView:
- convertRect:fromView:
What is the Android equivalent? More generally, given two Views, how do I get the second View's rect in the coordinate system of the first?
I don't think there is an equivalent as part of the sdk, but it seems like you could write your own implementation very easily using getLocationOnScreen:
public static Point convertPoint(Point fromPoint, View fromView, View toView){
int[] fromCoord = new int[2];
int[] toCoord = new int[2];
fromView.getLocationOnScreen(fromCoord);
toView.getLocationOnScreen(toCoord);
Point toPoint = new Point(fromCoord[0] - toCoord[0] + fromPoint.x,
fromCoord[1] - toCoord[1] + fromPoint.y);
return toPoint;
}
public static Rect convertRect(Rect fromRect, View fromView, View toView){
int[] fromCoord = new int[2];
int[] toCoord = new int[2];
fromView.getLocationOnScreen(fromCoord);
toView.getLocationOnScreen(toCoord);
int xShift = fromCoord[0] - toCoord[0];
int yShift = fromCoord[1] - toCoord[1];
Rect toRect = new Rect(fromRect.left + xShift, fromRect.top + yShift,
fromRect.right + xShift, fromRect.bottom + yShift);
return toRect;
}
In Android it might not have the method that exactly like that
however, you could do by
View.getLocationOnScreen(int[] location) and View.getLocationInWindowint[] location()
which
getLocationOnScreen(int[] location)
Computes the coordinates of this view in
its window.
getLocationInWindow(int[] location)
Computes the coordinates of this view on
the screen.
or View.getLeft() and View.getTop()
which
getLeft()
Left position of this view relative to its parent.
getTop()
Top position of this view relative to its parent.
for you case, I would use getLeft() and getTop() to find it's space between 2 View and get range of it
this link has an example of how to find it using getLeft() and getTop()
private int getRelativeLeft(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}
private int getRelativeTop(View myView) {
if (myView.getParent() == myView.getRootView())
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}
The way to do this is with getGlobalVisibleRect(Rect r, Point globalOffset).
/**
* If some part of this view is not clipped by any of its parents, then
* return that area in r in global (root) coordinates. To convert r to local
* coordinates (without taking possible View rotations into account), offset
* it by -globalOffset (e.g. r.offset(-globalOffset.x, -globalOffset.y)).
* If the view is completely clipped or translated out, return false.
*
* #param r If true is returned, r holds the global coordinates of the
* visible portion of this view.
* #param globalOffset If true is returned, globalOffset holds the dx,dy
* between this view and its root. globalOffet may be null.
* #return true if r is non-empty (i.e. part of the view is visible at the
* root level.
*/
So you give it a Point and get the offset relative to the root View. They key is that your views get a location relative to a common item. You can then use this offset to calculate relative positions. The doc says to get local coordinates, you offset the Rect with -globalOffset. If you want to get the second view's Rect in coordinates of the first, then offset the Rect of the first view with -globalOffset of the second view.
Similarly, you can use the globalOffset to transform a point to relative coordinates if you know it's coordinates in the root view. This should be a subtraction: anyPoint - globalOffset.
Remember to check that getGlobalVisibleRect() returned true and that globalOffset is not null before you do the calculations.

Layout flicker when dragging view in its MotionEvent.ACTION_MOVE android

Here is a picture of what I'm trying to do:
So, I want to resize a single cell while dragging resize anchors (black quads) which are ImageViews. To do this I attached custom onTouchListener to them that does next in MotionEvent.ACTION_MOVE:
calculate drag offset
set cell height/width based on this offset
reposition anchor point by changing it's layout params
The outcome of this is that the cell resizes but there is some king of flicker, more like shaking left/right or up/down, by some small offset.
My guess is that the problem comes when it catches move event, then I manualy change position of anchor and then, when it catches move event again it doesn't handle that change well...or something
I have an idea to put invisible ImageViews under each anchor and do resize based on their movement but do not move that anchor while draging. Then when I relese it, it lines up with coresponding visible anchor. But this is more hacking than solution :)
And finally, does anubody know why is this happening?
EDIT:
Here is the code where I'm handlign move event:
float dragY = event.getRawY() - resizePreviousPositionY;
LinearLayout.LayoutParams componentParams = (LinearLayout.LayoutParams)selectedComponent.layout.getLayoutParams();
LinearLayout parrent = (LinearLayout)selectedComponent.layout.getParent();
View topSibling = parrent.getChildAt(parrent.indexOfChild(selectedComponent.layout) - 1);
LinearLayout.LayoutParams topSiblingParams = (LinearLayout.LayoutParams)topSibling.getLayoutParams();
if(dragY > 0 && selectedComponent.layout.getHeight() > 100 ||
dragY < 0 && topSibling.getHeight() > 100)
{
componentParams.height = selectedComponent.layout.getHeight() - (int)dragY;
topSiblingParams.height = topSibling.getHeight() + (int)dragY;
//bottomSiblingParams.height = bottomSibling.getHeight();
selectedComponent.layout.setLayoutParams(componentParams);
repositionResizeAnchors(selectedComponent);
}
resizePreviousPositionY = event.getRawY();
and here is where I reposition it:
if(((LinearLayout)viewHolder.layout.getParent()).getOrientation() == LinearLayout.VERTICAL)
{
leftMargin = ((LinearLayout)viewHolder.layout.getParent()).getLeft() +
viewHolder.layout.getLeft() + viewHolder.layout.getWidth()/2;
}
else
{
leftMargin = viewHolder.layout.getLeft() + viewHolder.layout.getWidth()/2;
}
topMargin = viewHolder.layout.getTop();
params = (RelativeLayout.LayoutParams)resizeTop.getLayoutParams();
params.leftMargin = leftMargin - dpToPx(resizeAnchorRadius/2);
params.topMargin = topMargin - dpToPx(resizeAnchorRadius/2);
resizeTop.setLayoutParams(params);

Android: Total height of ScrollView

I have a custom ScrollView (extended android.widget.ScrollView) which I use in my layout. I want to measure the total height of the contents of this scrollview. getHeight() and getMeasuredHeight() don't give me correct values (too high numbers).
Background information: I want to determine how far the user has scrolled. I use onScrollChanged to get the X value, but I need to know a percentage so I'll need the total scrollbar height.
Thanks a lot!
Erik
A ScrollView always has 1 child. All you need to do is get the height of the child to determine the total height:
int totalHeight = scrollView.getChildAt(0).getHeight();
See the source of ScrollView. Unfortunately, this method is private, but you could copy it into your own code.
Note that other answers don't take padding into account
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0,
child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
}
return scrollRange;
}

Categories

Resources