Recyclerview get item by screen position x/y - android

Is it somehow possible to get an item of a recyclerview at a specific screen position (x and y coordinate)?

You can get View of an item at specific coordinates with this method findChildViewUnder.
View itemView = recyclerView.findChildViewUnder(x, y);

Related

How to animate only selected items in RecyclerView

I have RecyclerView and I want to animate one View inside RecyclerView item only for items which meet certain condition.
I'm using ObjectAnimator for this. Sometimes it is working fine, but sometimes animation is applied to all views. I assume there is some recycling problem with Views, but I'm not certain what can cause that.
Example:
val halfHeightCropped = halfHeight - (stopIndicatorHeight/2) //half item height minus half timeline circle height
val halfDistCurrToNext = timeDistCurrToNext/2
if (alreadyTraveled > halfDistCurrToNext){
animator = ObjectAnimator.ofFloat(indicator, "translationY", halfHeightCropped.toFloat()).apply {
duration = 0
start()
}
}
I'm an animating circle that is moving vertically through a timeline based on real-time. This circle is part of each RecyclerView item and in some items, this item is stationarily centered in item View, and some (1 at the time) is animating this circle vertically.
The example above is part of the function which is called from onBindViewHolder if the item meets the condition, otherwise, the view is reset to a default position. (layout param center vertical in an item).
when item select from recyclerView select that into adapter like
yourAdapter = new yourAdapter(new ArrayLis(),new ItemOnClick(){
#Override
public void onClick(int position){
yourAdapter.selectItem(position)
}
});
perform animate in bind() method of holder class.
if(selectedItem = getAdapterPosition){
//perform your animation for the selected item
}
your adapter should have the selectedItem variable and setter method for that.

Translate animation from position Y to top of the screen

I have multiple views that are all selectable. When one of them gets selected I want my new view to be first positioned to the same location as the selected one and also move it to the top of my screen. How can I do that? The position of selected item varies.
// Selected item
int[] location = new int[2];
item.getLocationOnScreen(location);
int positionY = location[1];
// Move the view I want to move to the position of selected item
viewToMove.setTranslationY(positionY);
// Create and start animation
Animation slideUp = new TranslateAnimation(viewToMove.getTranslationX(), viewToMove.getTranslationX(), positionY, -positionY);
slideUp.setDuration(1000);
slideUp.setFillEnabled(true);
slideUp.setFillAfter(true);
viewToMove.startAnimation(slideUp);
With ViewPropertyAnimator its easy :
viewToMove.animate().translationX(...).translationY(0).setDuration(1000);
translationY(0) is the top of the Y axis, and
at translationX(...) you fill in the coordinates where you want the View to move to on the X axis.

How to get an item which is at the center of list view?

I have listview ,I want to get item which is at the center of listview.
Example ListView
Having 1,2,3,4,5,6,7 is visible on screen.Now on click of button I want 4(4 is at the cent of visible list) should get highlighted.
Do this inside onClick() method of the button.
int midItem = ((listView.getLastVisiblePosition() - listView.getFirstVisiblePosition()) / 2) + 1;
View v = listView.getChildAt(midItem);
v.setBackgroundColor(0xFF00FF00); // Any color you want
It would highlight the item in mid of all visible items.

Animation for a GridView

Anybody knwos, how I can animate my GridView after deleting 1 entry? I want something like this:
The first is my normal gridview with 8 items. If I'm deleting one item, I want, that a animate starts and the coloumn of the deleted item will slide up and fill the free place.
Anybody an idea?
Thanks for help :)
One of possible ways to achieve that can be the following set of steps in given order:
1) Save left/top positions of all grid children before deletion. One thing to note here is that the left/top positions should be mapped not to the child position in gridview (which will change after deletion), but to the itemId in adapter which is going to be same for each child even after deletion. To do so, adapter should support stableIds;
2) Delete grid child from adapter and call notifyDataSetChanged();
3) Right after previous step add OnPreDrawListener to the gridview. That is good place to initialize animations, as the layout is ready but not yet drawn to screen. Now we can access all children's coordinates after deletion. On the first onPreDraw() callback, remove listener and calculate diffX and diffY values of each child. And set translationX, translationY values to diffX and diffY and start animating each child to 0 translation value.
Pseudo code might look like this:
// Step 1
saveTopLeftPositions(gridView);
// Step 2
adapter.removeAt(deletedPosition);
adapter.notifyDataSetChanged();
// Step 3
gridView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
gridView.getViewTreeObserver().removeOnPreDrawListener(this);
int firstVisiblePos = grid.getFirstVisiblePosition();
int childCount = grid.getChildCount();
for (int i=0; i<childCount; i++) {
View child = grid.getChildAt(i);
long itemId = adapter.getItemId(i + firstVisiblePos);
float diffX = child.getX() - getOldXPos(itemId);
float diffY = child.getY() - getOldYPos(itemId);
child.setTranslationX(diffX);
child.setTranslationY(diffY);
child.animate().translationX(0).translationY(0);
}
return false;
}
});

How to detect when any child view recieves a click

Given an arbitrary ViewGroup G with an arbitrary collection of child views, how can I detect when the user clicks on any of the child views? In this case, I want to draw a highlight for G.
I could add an onClick listener for each child, but I'm trying to avoid that so that the code doesn't have to be changed when the layouts change.
Alternatively, I could add onTouch handlers to G and set the highlight during ACTION_DOWN. However, this would trigger for actions that don't actually result in clicks, such as a swipe (the swipe could be handled by ViewPager, for example, and ultimately be irrelevant to G).
My layout for G has the focusable attributes:
android:focusable="true"
android:focusableInTouchMode="true"
Thanks.
Here is how I do it:
//in onTouch method of parent, I get the coordinates of click
int x = ((int) motionEvent.getX());
int y = ((int) motionEvent.getY());
//obtain the clickable arrea of the child as HitRect
Rect clickRect = new Rect();
Rect rect = new Rect();
imageView.getHitRect(rect);
//ask if the area contains the coordinates of the click
if(rect.contains(x, y)){
//do some work like if onClickListener on the child was called.
return false; //you clicked here, don't need to handle other Childs
}
//ask for other childs like before...
Now, you can target the parent as the delegate of all clicks done inside it, even if it is done in a child.
EDIT:
To ignore other touch event that are not click, you can ask for how much user moved the finger:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
return true; // user mover finger too much, ignore touch
}
return false; // finger still there waiting for click
I give a square of 10 pixels to permit a confortable click, and if you exit it, I ignore it.
EXTRA:
Here is the complete code for click and long click with onTouchListener.
You could use the View.getChildCount() to loop through all child views and see if the touch intersects with the child view.
This involves getting x and y positions and calculating if it fits within the child view, use View.getChildAt(position) to get the reference to the child view .
So it would be something like this:
int childNr = theView.getChildCount();
for (int i = 0; i < childNr; i++){
YourView tmp = (YourView) theView.getChildAt(i);
if(tmp.intersects(x, y)){
do some work
}
}
here you would have to put your view variable instead of theView and the class name which handles the views instead of (YourView) and x, y are the coordinates of the pressed spot.
In your XML, you could add point all the children to the same onClick method. Inside that method you could draw the highlight to G and then do something (or nothing) for the individual child view.

Categories

Resources