I am writing a game in which i need to check collisions between two views. when i use the view.getHitRect(outputRect) method from some reason the bottom padding of the view (all the other paddings are set to 0) is added to the top property of the view's hit rectangle. I've tried to fix this by overriding the getHitRect method:
public abstract class GameView extends ImageView {
#Override
public void getHitRect(Rect outRect) {
super.getHitRect(outRect);
outRect.top+=getPaddingBottom();
}
This got me a better result, but its still inaccurate, and the bigger the padding gets the less accurate the rectangle becomes, and the collision always occur too low. Before using paddings I was trying to do it without it, which gave me accurate hitRect but i got a different bug. Thanks in advance
Solved it after some trial and error. Apparently the padding was added to the rectangle, but it was divided equally between the rect.top and rect.bottom, so the solution was:
#Override
public void getHitRect(Rect outRect) {
super.getHitRect(outRect);
outRect.top+=getPaddingBottom()/2;
outRect.bottom-=getPaddingBottom()/2;
}
If one of you Android experts care to explain why I would be grateful, since I don't understand how it works and I just can't see the logic in it.
Related
It seems that every angle I manage to find doesn't end up working in the way I need it to. My goal is to be able to customize the positioning and size of any scrollbar on any view, be it a recyclerview, gridview, or listview. I've tried using layer-list xmls to adjust the height and positioning, a Seekbar turned vertically, as well as trying to create my own scrollbar thumb and track using imageviews.
In terms of the layer-list, it just didn't have an effect on the scrollbar at all. The other two attempts at a solution (using a Seekbar, using individual imageviews) were nearly effective, except I needed the current scrolled position (getScrollY()) to be able to make the scrollbars I made actually accurate instead of just visually being a scrollbar. However, even though getScrollY() is defined for recyclerview, gridview and more, it always returns a 0, so I am unable to get that information (except for scrollviews, perhaps; I believe that's the only view type that properly returns a getScrollY() value).
Is it even possible to customize the scrollbar in this manner? I'd be keen to see references or documentation that can point me in the right direction. It feels like this is generally a non-issue for most developers on Android, or at least in general isn't something many people have asked for.
Edit
To assist in visualizing what I have and what I desire, here's a screenshot of the scrollbar as it is right now:
The following image is marked up to show what my intended outcome for this scrollbar would be:
Views have the capability for a scrollbar but a lot don't show them by default.
So any View has a whole load of XML attributes to customise the appearance, size and position.
But these are useless if not shown.
A lot of ViewGroups sub classes setWillNotDraw to be true and this removes the capability to draw the built in scrollbars of the View.
So to get any view to show it's built in scrollbars you need to the setWillNotDraw(false)
Getting any View to show it's built in scrollbars is Step 1 but again not all Views Calculate automatically the length and position of scroll hence they return 0 for the scroll position.
The View has to implement the following methods and return the appropriate numbers for the scroll position to be correct and things like getScrollY to return more than 0
// Length of scrollbar track
#Override
protected int computeHorizontalScrollRange() {
return (int) value;
}
// Position from thumb from the left of view
#Override
protected int computeHorizontalScrollOffset() {
return (int) value;
}
#Override
protected int computeVerticalScrollRange() {
return (int) value;
}
#Override
protected int computeVerticalScrollOffset() {
return (int) value;
}
Off Course some View sub classes don't use the built in ones but draw there own.
I am trying to create an activity that will display an image on the onClick of a button. So far, I have successfully made my application fade the image in when you click the button, but I want the image to be displayed over the button that has been pressed.
From my understanding you want to go about this using LayoutParams which I have attempted to do, but it seems to be getting the wrong values from my Button.getX() and getY(). Here is my code:
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ConstraintLayout.LayoutParams layoutParamsButton1 = new ConstraintLayout.LayoutParams(((int)button1.getX()), ((int)button1.getY()));
whatImage[0].setLayoutParams(layoutParamsButton1);
fadeInAndHideImage(whatImage[0]);
}
});
whatImage[] is just an array of ImageViews because I want to change their locations around, just fyi.
Anyway, what I think might be happening is a problem with the getX() and getY() methods retuning wrong values, because my buttons are constrained with android.support.constraint.Guidelines. Also, I don't think this is a problem, but I have to cast them as (int)s because they both return floats. There might be some rounding errors but I doubt getX() and getY() would return values that are by the half or quarter pixel.
Thanks
EDIT
Just tried to add ((int)uno.getX()), ((int)firstRail.getY()) into my LayoutParams instead of the buttons x and y, (uno and firstRail are guidelines that I use for the button), but it unfortunately put the button in the same exact spot as the LayoutParams with the buttons getX() and getY() methods.
Also, it seems to scale the image down when it moves it, which is also odd
EDIT 2
Ugh, after printing to out to see what exactly these values are, button1.getX() and firstRail.getX() both return 0.0. Is this even possible while using a Constraint layout??
Try:
whatImage[0].setX(button1.getX());
whatImage[0].setY(button1.getY());
Also keep in mind that resize of image could happend if you have some padding attribute added to your ImageView into xml
I have written two ItemDecorator's for RecyclerView. Each adds some top offset in getItemOffsets(). Let's say:
First decorator adds 20dp top offset
Second decrator adds 30dp top offset
Now, when I add both of them to RecyclerView, each item is correctly offsetted by 50dp, that's good.
But here comes the question:
How do I get this offset in onDraw/onDrawOver?
Usually decorators draw their stuff by traversing parent.getChildAt(i) stuff and getting child.getTop() for example to draw above child view of RecyclerView.
But in this case, doing so would mix up the drawing of other decorator, because it would also use child.getTop().
So at the moment it seems like both decorators need to know about each other and each other's height.
Am I missing something here? I hope I am.
EDIT: I reported an issue to Android issue tracker and it seems this will be worked on. Star it to keep track of progress: https://code.google.com/p/android/issues/detail?id=195746
tl;dr No you are not missing anything. But you can get the values needed in getItemOffsets, albeit it seems a little bit dirty to me.
Basically there is only one option of getting the decorated height other than managing decorations yourself: LayoutManager.getDecoratedTop();
onDraw
In onDraw you get the whole canvas of the recyclerView, c.getClipBounds() does not hold any information. Albeit the javadoc of adding decorations says that decorations should just draw withing their bounds.
Also, getting parent.getLayoutManager().getDecoratedTop() will give you the same values in every decoration, since it's already too late here, those values are for layouting purposes.
We are too late, layouting is done and we missed it.
getItemOffsets
Please note that I tested the following with a LinearLayoutManager and it might as well not work with the others (Most definitely not with most custom ones). Since I am relying on measuring happening between those calls, which is not documented, the given solution might break with some future version.
I just felt I needed that disclaimer. I found a working solution (watch the mOffset):
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
mOffset = parent.getLayoutManager().getTopDecorationHeight(view);
outRect.set(0, 10, 0, 0);
}
This works, because the recyclerview, when calculating the total inset of the child, is updating the views package local LayoutParams variable. While we cannot access the layout parameter variable itself, calling getTopDecorationHeight actually uses the (currently) dirty inset, giving us the proper value.
Hence, you can get the offset of the previous decorations before applying your own!
To apply it, just remove the other decorations offset when drawing your decoration:
c.drawRect(child.getLeft() + child.getTranslationX(),
mOffset + layoutManager.getDecoratedTop(child) + child.getTranslationY(),
child.getRight() + child.getTranslationX(),
child.getBottom() + child.getTranslationY(), paint);
This will actually apply the other decorations offset, then draw your own from the correct top.
Some Problems
This is now a basic, working solution for the default usecase. BUT. If you have different offsets depending on the item VIEW_TYPE or the adapter position things get tricky.
You will either duplicate your logic and keep various offsets for various view types or you have to store / retrieve the offset for every view or viewtype.
To do so, you could either add some custom tag with view.setTag(key, object) or doing something similar with the RecyclerView.State object that's getting passed around.
Within Android, I'm trying to move a TextView from outside the parents bounds into view, but the contents never shows up, or remains clipped if it was partially within the bounds already.
Initial situation at start
Situation after animation
(Below this is another view, that was completely out of bounds and isn't drawn either)
I have 4 TextViews below each other in a custom Object extending RelativeLayout. Based on a percentage the top 2 should move outside it's bounds and the bottom 2 should move in (from the bottom).
I use the following code to update the properties of each TextView. In this class each variable **positionY* is filled with their initial position from the layout-xml. effect is percentage between 0 & 1. The animation works, but the views aren't drawn again.
public class ActionBarTitleView extends RelativeLayout {
public void updateTransition(float effect) {
float height = getHeight();
titleView1.setY(title1positionY - height*effect);
detailView1.setY(detail1positionY - height*effect);
titleView2.setY(title2positionY - height*effect);
detailView2.setY(detail2positionY - height*effect);
invalidate();
}
}
What I tried
After some researching I found a few hints what the issue might be, but so far none of the tried options had any effect. Below is a list of things I've found on SO and tried.
Calling invalidate() on the RelativeLayout - No effect.
Invalditing the TextViews - No effect.
clipChildren = false for the RelativeLayout - No effect.
setWillNotDraw = false for the RelativeLayout - No effect. (onDraw is being called)
I haven't tried to solve this with a ScrollView, but I don't want to really, cause that adds another layer in the hierachy for something pretty small.
I thought I understood the drawing logic, but perhaps I'm missing something, so I hope someone might be able to point me in the right direction.
What I ended up doing (September 3rd)
Since no real solution was offered, I tried again and came to the following "fix". I set both second labels to Visibility.GONE, but within the original bounds of the container view. Then when I start the animation, I set their correct values, then move them outside the bounds and finally setting Visiblity.VISIBLE. When the animation progresses the labels roll into view as supposed to. So a fix to the issue, but no real explanation why the TextViews aren't drawn again...
I have a custom FrameLayout class which does some custom drawing on the OnSizeChanged event. It's done there and not in the OnDraw event to avoid reentrancy and therefore performance issues. This works fine in a single activity application but fails in a TabActivity. The activity in the current tab renders fine but activities in non-active tabs are not being rendered. Here you can download an example project reproducing this. The project is HelloTabWidget but uses HelloAndroid project as well.
Found that onWindowVisibilityChanged event might help but need to find adequate size to plot my control. In the code snippet below I'd need a way to find container's size at onWindowVisibilityChanged.
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
int width = this.getWidth();
int height = this.getHeight();
drawContent(getCtxt(), width, height);
}
The solution was using OnDraw event doing some refactoring to the component and using a boolean flag to determine when the canvas is dirty to avoid unneeded reentrancy. SetWillNotDraw had to be set to false as well.