Scroll ListView to coordinates of click, like QQ - android

I want to scroll to specific item depending upon where user has clicked. All my items of list view are of different heights. Please see the image below.
So if user clicks on the list item 1, lets suppose, so it's bottom should be scrolled till the top of the softkeyboard.
Heights of items and keyboard vary. I have tried getTop, getHeight, getMeasuredHeight but nothing has been working so far.

Main point is to find the distance between the clicked UI and your keyboard top.
This is the distance formula. You can achieve your functionality using this. You need
Clicked Coordinates
Coordinates of your listview item
Height of your listview item
Suppose following is your ListView, that is being clicked
int[] loc = new int[2];
lvItem_button.getLocationOnScreen(loc);
Put a layout at the bottom of your layout, mark your activity as AdjustPan in Android Manifest, now it will always be at the top of keybaord, when ever the keyboard will open.
Get it's coordinates the same way.
int[] cords= new int[2];
keyboard_top_layout.getLocationOnScreen(cords);
double distance = Math.sqrt(Math.pow((cords[0] - loc[0]), 2) + Math.pow((cords[1] - loc[1]), 2));
distance -= heightOfItem; //subtract height because we want to align bottom of item to top of keyboard
distance = Math.ceil(distance);
lvItem.smoothScrollBy((int) -distance, 500); //-distance because X is increasing at bottom and decreasing at top. 500 is delay in scrolling. so it's smooth scroll
All done :)

Related

Android ViewPager - Dynamic width & multiple visible pages

My issue is quite complex and I have hard time finding a solution to achieve the following :
I need to have a sort of legend (a stepper) layout where a child is made of left bar, followed by a circle, followed by a right bar. Strickly speaking somethind like "-o-". So a serie of children looks like this : "-o-o-o-o-o". When the legend focuses on a child meaning that the current child is this one, this child has a bigger circle and longer bars: "--O--".
The final rendering looks therefore like this : "-o-o--O--o-o-".
Each child ("-o-") corresponds to a specific item in a database. When a child gets focuses ("--O--"), the relative item is displayed below. When the user swipes from right to left, the next item is showed, whereas a swipe from left to right will display the previous item.
When swiping, to show which child will display its corresponding item, say child will be animated to show it has focus. I use a Animator to :
increase the bars widths and the circle size of the child that is
getting the focus
to decrease these values of the previously focused child.
Displaying a legend requires to see what's before and what's next.
I believe using a ViewPager will help me achieve the following :
it has an "anchor effect", meaning a swipe will move from child to child, centering on the circle: the circle between the bars is the anchor, so to speak.
I can display multiple pages on the screen by overriding the getPageWidth() method of my custom adapter
The other solution I had in mind is the HorizontalScrollView.
If I were to use it, depending on the swipe properties (strengh, speed), the legend would indeterminately scroll without anchoring the circle.
But using the ViewPager has a big problem: each page has the same width. However, the anchored child of the legend needs to be bigger (so needs more width).
So how can I change the width dynamically ?
Unless I can customize a HorizontalScrollView to add the anchor effect by intercepting in swipe ?
There is also the CarouselLayoutManager which I think may include the effect you desire.
If you are using a ViewPager, I think the best way to do this would be with an OnPageChangeListener. You will get callbacks as the user is swiping left/right so that you can size your indicators based on the location of the user's swipe.
private int mCurrentPage = 0;
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// positionOffset goes from 0.0 to just under 1.0 for the leftmost page
float otherOffset = 1.0 - positionOffset;
if (position == mCurrentPage) { // user is incrementing page, i.e. swiping left
// size of current page indicator based on otherOffset
// size of next page indicator based on positionOffset
} else { // user is decrementing page, i.e. swiping right
// size of current page indicator based on positionOffset
// size of previous page indicator based on otherOffset
}
}
#Override
public void onPageSelected(int position) {
mCurrentPage = position;
// ... set final sizes: page # position = 1.0 all others 0.0
}
});
Here's how to understand position and positionOffset: When a page has settled, it is centered. Let's say the current page position == 1 (i.e. page 2).
First case: User swipes left to get to the next page. Say the user swipes 10% of the distance to get to the next page. position will be 1 (leftmost displayed) and positionOffset will be 0.1 (viewport left edge is at 10% of leftmost page). So as user swipes, positionOffset will go from 0.0 to just below 1.0.
Second case: User swipes right to get to the previous page. When the user swipes 10% of the distance to the previous page, position will be 0 (leftmost displayed) and positionOffset will be 0.9 (viewport left edge is at 90% of the leftmost page). So as user swipes, positionOffset will go from just under 1.0 to 0.0.
As for the indicators themselves, I did a similar pager indicator where I used a LinearLayout with a horizontal orientation, adding a child indicator view for each page. I used pagerAdapter.getCount() to get the number of views to add.
Your indicator is more complex with the animation, but if I were to do this, I would use a plain View with ScaleDrawable backgrounds for graphics that could change size.

Get position of ListView scrollbar thumb on screen

I am trying to get the location on screen of the ListView scrollbar thumb indicator. I'd like to position a label next to it and require the X,Y screen coordinates.
I attempted to get the height of the ListView and use the value from the 'firstVisibleItem' value obtained from the ListView OnScrollListener to position the label but due to the ListView items having varying heights this hasn't proved feasible.
Are there any other solutions to this?
Thanks
I suggest you can use a little bit of hack:
View c = listview.getChildAt(0);
int scrolly = -c.getTop() + listview.getFirstVisiblePosition() * c.getHeight();
But it will work correctly only if you will have same height for all list items.

Move button back to origin after dragging/moving it

I have an ImageButton that I need aligned to the bottom of the screen, and when I click and drag it, it moves with my finger by a certain distance, beyond which it will not move further from the origin but continue following the direction my finger is from the origin.
When I let go, the button has to slide back to the origin.
What I have managed to do so far is to have the button follow my finger via this answer: Moving buttons via Touch
However, how should I get the origin of the button at initialization for it to bounce back after I release?
Originally I implemented the alignment of the button by having a android:layout_gravity="center|bottom" to place it at the bottom, but it somehow messes with the position of the button relative to my finger (It follows my finger movements but it's off center).
Hence, I used mButton.setY() and mButton.setX() to place it at the bottom of the screen, but it seemed hackish to use the screen dimensions to place it. Can't think of a better way.
You can set the width and height of your layout in XML using
layout_width and layout_height.
Then in your MainActivity, instantiate the layout (I will use LinearLayout as an example) and get its width and height.
LinearLayout l = (LinearLayout)findviewbyid(R.id.l1);
int height = l.getHeight();
int width = l.getWidth();
Now, whenever you release the button, you can set your its position in the following way:
mButton.setY(height - 50)
This will position the ImageButton 50 pixels above the bottom of your layout. You can also set the x position of the button as you want in the same way. Also, you should probably store height - 50 as a global variable (origin) that you can call from anywhere in your code.

How can I get the y position of an item in an Android listview in the overriden 'getView' function?

I have a customlist in which I overload the getView function and I need to know the y position of the current item that is being drawn. And I need it in relation to the entire list and not just the ones visible on screen. How can I find this out?
Edit: The Items have different heights.
Since you want the one relative to the top of the list (not the top of the visible screen, you can calculate it using:
int yPos = view.getHeight() * position;
Edit:
Different heights? That probably hurts performance, since the recycler isn't helping much.
I don't think there's a magic method for this case. Realistically what you have to do is keep an array of list position and y offsets, so for instance
// Item at position 0, top starts at 0, height is 15
pos[0][0] = 0
pos[0][1] = 15
// Next item starts one pixel down from previous item's top, is 10 pixels tall
pos[1][0] = 16
pos[1][1] = 10
And just populate from within getView. Note that this solution tracks individual height as well as absolute offset for each item- since the previous item's position + height is required to determine the next item's position.

GridView Scroll By one row

Hi i am implementing a gridView and i have trouble making it scroll down 1 row at a time with every scroll event.
my grid has a height of 1 row item (items height is 75dp). i don't want scrolling to be left in a middle of a row.
is there a way i can intercept and modify the scroll distance so that it only returns fixed value ex: +-75dp.
i would appreciate any help or suggestions you can give me. tnx
APIv8 has new function, called smoothScrollBy(int distance, int duration)[1]
i think you should catch all scroll events & implement own method to scroll view.
if you want to scroll by 75dp, just convert it to pixels & use function above.
float density = getContext().getResources().getDisplayMetrics().density;
int scrollBy = (int)(density * 75);
smoothScrollBy(scrollBy, 0);
But would be nice to calculate scrollBy from your GridView, instead of using some constant value (like 75dp)
[1]: http://developer.android.com/reference/android/widget/AbsListView.html#smoothScrollBy(int, int)

Categories

Resources