I have a question about the FragmentGridPagerAdapter. When I override the public Fragment getFragment(int i, int i2) function and log the i and i2 values, i get these strange results:
When i swipe right i2 get's increased, so it goes 0, 1, 2 etc. That makes sense.
But let's say I swiped 6 times to the right and then 1 below I would expect i2 to be 6 and i to be 1. However, i2 get's reset to the first position immediately to the first position. Does anyone know what causes this or what's the logic behind this is?
This isn't an issue with the getFragment API, this is the actual behavior of the GridViewPager element. It doesn't really behave like a grid - swiping up or down always starts at the first column.
Presumably this was implemented to enforce Google's design guidelines for a 2D picker, which says "Navigation should be vertical-then-horizontal, not horizontal-then-vertical."
Just to make it more readable: the i should be named row and i2 should be called column.
But to the point:
The GridViewPager is implemented to help you reproduce the Android Wear Home stream behavior. As you know, there are notification cards in the first columns and actions to the right hand side. If you have scrolled to the right to second or third action page for particular notification it would be very confusing to the user if (after swiping up or down) he would land in the middle of action pages for completely different notification. He would see the some action but it would be hard to guess what notification it refers to. So they implement it that way so it resets the current column and returning always to the column with index 0.
This behavior is presented in a video about Building Advanced UIs for Android Wear.
About 2:50 this topic is started (the actual explanation is near 3:25 if you want to skip), but I recommend to watch the entire video.
To change this behavior you simply need to override getCurrentColumnForRow(int row, int currentColumn) method in your FragmentGridPagerAdapter (or just GridViewAdapter).
Here is the documentation for that method:
Returns the column to arrive at when navigating vertically to the
specified row.
The default implementation simply returns 0.
Parameters
row the row in question
currentColumn the column which is currently centered
Returns
the column to arrive at
To achieve what you want you just need to return currentColumn, or maybe do some check before returning it to see if this position is valid for the new row.
Related
I have a screen where items/cells are designed in the 2x2 grid form. What I want to do is I want to fire APIs on item visibility/impression when RecyclerView scrolls up/down.
As my list is pretty huge
For example scenario is,
On the first load, it shows e.g. 6 (3x2) items/cells. Then, fire API for all those 6 items as those are fully visible.
On scroll up, e.g. it shows another 2 items and above/top 2 items go out of screen hence, it should fire API for newly 2 items.
This way I want to complete traversal of the entire list and call API on newly found items.
What I tried is,
Below methods of GridLayoutManager not worked for me as each of them giving different values and seems to be random
int findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();
Checking bounds of View lying on screen with the help of Rect | getGlobalVisibleRect() but that is also not working inside onScrolled() | OnScrollStateChanged()
Please suggest approach where I can get the correct index of the fully visible cell.
Try to create separate thread for the action of onScrolled() | onScrollStateChanged(). It will solve your problem.
I am currently designing a wearable app that uses a GridViewPager with 4 rows and 5 columns.
I have several questions regarding the scrolling:
Is it possible with a Java function to go directly to a specific line and column? I tried scrollTo but it isn't working.
Is it possible when changing line to always go in the second column or to stay in the same one instead of always coming back to the first one?
Or is it possible to go to another layout when swiping from left to right when on the first column instead of dismissing the app but with the ability to dismiss the app from this new activity?
For part 2 of your question, you can override getCurrentColumnForRow(int row, int currentColumn) in a custom GridPagerAdapter and set it to return currentColumn. That will allow it to stay in the same column when changing rows.
Regarding part 1 of your question:
scrollTo is used to change a view's position on the screen.
You can call GridViewPager.setCurrentItem(row,col) instead.
My app has a ListView where data is added from the top, automatically (through setData on a Adapter). When data is set, usually most of the data remains the same, and only a few items are added at the top. Typically tens of items exist, and 1-2 items are added at the top every ~10 seconds.
Right now the user experience sucks. It completely re-draws the list with the new data. If a user was reading an item just before data got updated, he will lose his old position and will need to scroll to find it again.
What I would like to see as a more human compatible transition / animation, where the old data gets pushed down, and new items are added at the top. Or - new data pushes old data 'down' - and I can see this animation.
(Something like a 'ticker' - only vertical, and items are on a listview.)
I went through ListViewAnimations, and JazzyListView. They are very nice, but they don't support my requirement.
I hate re-inventing the wheels, so after coming up empty I wanted to make sure there really isn't anything out there.
If a user was reading an item just before data got updated, he will
lose his old position and will need to scroll to find it again.
Before updating the list view remember the position and restore it.
Some thing like this:
private int position;
private void save(){
position = myListView.getFirstVisiblePosition();
}
private void restore(){
myListView.setSelection(position);
}
You should check this, its from Android Developer page:
http://developer.android.com/training/animation/layout.html#activity
You might be able to getFirstVisiblePosition before calling setData, then setSelection after. That will still jump slightly (snapping to the current first visible item), unfortunately I believe you need to subclass ListView to access the pixel scroll value (getScrollY is for the view itself, not for scrolling inside it).
Maybe consider letting the user decide when to reload? For instance, display a list header "3 new items, swipe down to refresh" or some such.
I just have a question regarding changing an Activities layout.
Basicly my problem is:
I have a list view populated with a series of Strings.
I have an OnItemClick event assigned to the ListView,
When the user clicks an item in the list view, I want the current layout to disapear and an image to take its place.
From reading other posts, I understand that the recommended way is to set a separate activity for each item in the ListView, however, seeming as all the activity is doing is displaying an image I think it would be a waste of effort to set up a separate activity for each item in the ListView...
Could anyone give me some help on this?
thanksin advance.
Shaw
You really should create a new activity (ar a new fragment if you have space, but this is another question) which displays the image, for at least the following reasons
it is very, very simple to code and mantain such a solution. What if tomorrow you want to add 2 buttons and some text and maybe a menu for that image? you have your brand new activity to edit and upgrade without risking to damage your list activity.
it is more user friendly. If the user presses back when the image is shown, with 2 activities he will be back to the list, with your solution he will go back BEFORE the list, and this is not what he'd espect
remember this piece of advice: 1 activity = 1 simple task or interaction with user.
list + image display = 2 activities (or fragments)
PS: you do not need to define a different activity for each list item, just pass with the intent to the "ImageActivity" and specify there which image to show!
EDIT 2: to pass to the next activity your current selection, just use the putExtra(String key, T value) (T may vary, check documentation) method of Intent class.
example: intent.putExtra("imgCodeSelected", index) where index is fetched by the onItemClick event. You can put as many extras as you want as long as they have different keys.
I am developing an application that needs to show calendar agenda, much like the agenda in the native calendar. I have a list view showing different events (Note: in the context of the question events is entity of my system). I want to provide a 'Today' button on this screen. When the user clicks on this button the events are supposed to be scrolled until the first event of the current's day schedule is on top of the screen. The problem occurs when I have only a few events scheduled from today on - so few that they do not fill a whole screen. Then the list view just scrolls until the last event in the calendar is on the bottom. This usually means that the desired effect of having the first today's event on top is not achieved.
Any suggestions how this can be done? I have thought of adding some blank elements at the end, but this seems ugly workaround, and furthermore it will require special device-specific calculations that will tell me how many elements to insert.
Edit:
Adding some code as requested in comment
Actually I am not sure this code will surprise anyone, but:
public void onTodayClicked(View target) {
// calculate the indexOf. It works and is not related to the question
if (indexOf >= 0) {
ListView list = (ListView) findViewById(R.id.events_list_view);
list.setSelection(indexOf);
}
}
I am not sure the layout definition is important to aid the answering of the question, but if you think so I can add it too.
You can achieve this by two ways:
call smoothScrollToPositionFromTop method
call setSelectionFromTop method
Using the smoothScroll method is better, because it actually does the transition smoothly - that means it really scrolls to it.
The only downside is that it's only available after API level 11.
The setSelectionFromTop method works since API level 1, but instead of smoothly scrolling, it jumps to the row.
If you don't need to position to the top of the screen, only to view the row, you can also use smoothScrollToPosition, which is an API level 8 call.
If you give these methods the position, which is the FIRST in the list, they will work well. (From your description I think you probably calculate the last position, but I can't be sure).