I have a layout above a list view which is at the bottom of the main layout.
When i scroll the list, i want to make the top layout smaller. i am trying now to do that using on list view scroll listener and change the layout parameters width and height.
The problem is that this procedure its not smoothly at all, eats a lot of resources.
How can i re-size the top view without using layout parameters (eating a lot of Resources) ?
that the scroll listener I set for the listview:
public class EndLessScrolling implements AbsListView.OnScrollListener {
private int amountViewInitDimension = 0;
public EndLessScrolling() {}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//amountView on scroll resize effect
if (firstVisibleItem < 5) {
View amountView = getActivity().findViewById(R.id.select_receiver_amount_parent);
if (amountView != null) {
ViewGroup.LayoutParams params = amountView.getLayoutParams();
if (amountViewInitDimension == 0) {
amountViewInitDimension = params.width;
}
params.width = amountViewInitDimension - firstVisibleItem * amountViewInitDimension / 10;
params.height = amountViewInitDimension - firstVisibleItem * amountViewInitDimension / 10;
receiverAmount.setTextSize(TypedValue.COMPLEX_UNIT_SP, amountTextSize - firstVisibleItem * zoomStep);
dollarSign.setTextSize(TypedValue.COMPLEX_UNIT_SP, dollarSignTextSize - firstVisibleItem * zoomStep / 2f);
amountView.setLayoutParams(params);
}
}
}
}
Have you seen this library? I think is what you want.
https://github.com/carlonzo/StikkyHeader
Related
I have 2 listview controls that each display part of the data of a list. They are oriented above and below each other in a UI fragment. The data displayed is tire information for a car, so the top listview displays data for the front wheels and the lower listview displays data for the rear wheels (with a car image between them). The listviews are configured so that the data list in each view mirrors the data in the other. So the top line of the top list is of the same dataset as the bottom line in the bottom list. When the user scrolls either list, the other list scrolls in a mirrored fashion. This enables the user to scroll the data such that the data they are interested in is positioned directly above and below the car image (bottom row of top list, top row of bottom list)
To accomplish this, I'm responding to the OnScrollListener-OnScroll event on one listview, making position calculations and calling listview.setSelectionFromTop(position, sety) on the other listview to update its position.
This is working fine in Android 4.xx when I first released the app. Now in Android 7 and higher, it appears that a race condition is occurring where setting the position on one listview is triggering an OnScroll on the other and things go bad from there in a never ending series of OnScroll events.
I've tried adding a flag on the OnScroll function so that it won't trigger if its inside an OnScroll call from the other listview. This didn't work as it appears that the OnScroll events triggered from calling setSelectionFromTop are asynchronous so they occur outside the flagged area.
One thing I've noticed is that it appears that the sizes of the listviews is critical and that they both need to be the exact same size. In the original layout (Android 4.x) I've set their layoutHeight=120dp to fix their size. However, current android doesn't appear to respect the layoutHeight values and the resulting UI shows them as different sizes, this may be a key to the puzzle.
Here's the onScroll code for one of the listviews, the code for the other is the same, just replace lvTop with lvBottom
OnScrollListener lTop=new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (fInBottomScrollListener)
return;
fInTopScrollListener=true;
// Get position of this lv top
View cTop = lvTop.getChildAt(0);
View cBottom = lvBottom.getChildAt(0);
if (cTop==null || cBottom==null) return;
int topOffset = cTop.getTop();
int itemHeight = cTop.getHeight();
int firstVisPos = lvTop.getFirstVisiblePosition();
int setposition = totalItemCount - (visibleItemCount - (topOffset!=0 ? 1:0)) - firstVisPos-1;
int sety= -(itemHeight + (topOffset>0 ? 0:topOffset));
lvBottom.setSelectionFromTop(setposition, sety);
fInTopScrollListener=false;
}
};
I see in the log, a continuing list of onScroll events, when running on Android 4.X this list stops after scrolling is complete, whereas on 7.x and greater, it continues and scrolling is basically frozen on both listviews until I refresh the fragment
I think that your calculations for the position and offset are incorrect, so the 2 ListViews are bouncing beacuse of a small offset error.
For ListViews with same heights:
AbsListView.OnScrollListener lTop = new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInBottomScrollListener) {
fInTopScrollListener = true;
View cTop = lvTop.getChildAt(visibleItemCount - 1);
if (cTop == null) return;
int setposition = totalItemCount - visibleItemCount - firstVisibleItem;
int sety = view.getHeight() - cTop.getBottom();
lvBottom.setSelectionFromTop(setposition, sety);
fInTopScrollListener = false;
}
}
};
AbsListView.OnScrollListener lBottom = new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInTopScrollListener) {
fInBottomScrollListener = true;
View cBottom = lvBottom.getChildAt(visibleItemCount - 1);
if (cBottom == null) return;
int setposition = totalItemCount - visibleItemCount - firstVisibleItem;
int sety = view.getHeight() - cBottom.getBottom();
lvTop.setSelectionFromTop(setposition, sety);
fInBottomScrollListener = false;
}
}
};
Updated:
This is another approach, no matter the listviews' heights are same or not:
final private static int SCROLL_DELAY = 100;
long timestampScroll;
AbsListView.OnScrollListener lTop = new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInBottomScrollListener) {
fInTopScrollListener = true;
View cTop = lvTop.getChildAt(0);
View cBot = lvBottom.getChildAt(0);
if (cTop == null || cBot == null) return;
int lvTopMovableRange = (cTop.getHeight() + lvTop.getDividerHeight()) * totalItemCount
- lvTop.getDividerHeight() - lvTop.getHeight();
int lvBotMovableRange = (cBot.getHeight() + lvBottom.getDividerHeight()) * totalItemCount
- lvBottom.getDividerHeight() - lvBottom.getHeight();
int lvTopPointer = (cTop.getHeight() + lvTop.getDividerHeight()) * firstVisibleItem
- cTop.getTop();
int lvBotPointer = (int)((float)(lvTopMovableRange - lvTopPointer)/lvTopMovableRange*lvBotMovableRange);
int setposition = lvBotPointer / (cBot.getHeight() + lvBottom.getDividerHeight());
int sety = -lvBotPointer + setposition * (cBot.getHeight() + lvBottom.getDividerHeight());
timestampScroll = System.currentTimeMillis();
lvBottom.setSelectionFromTop(setposition, sety);
lvTop.postDelayed(new Runnable() {
#Override
public void run() {
if (System.currentTimeMillis() - timestampScroll > (SCROLL_DELAY - 10)) fInTopScrollListener = false;
}
}, SCROLL_DELAY);
}
}
};
AbsListView.OnScrollListener lBottom = new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!fInTopScrollListener) {
fInBottomScrollListener = true;
View cTop = lvTop.getChildAt(0);
View cBot = lvBottom.getChildAt(0);
if (cTop == null || cBot == null) return;
int lvTopMovableRange = (cTop.getHeight() + lvTop.getDividerHeight()) * totalItemCount
- lvTop.getDividerHeight() - lvTop.getHeight();
int lvBotMovableRange = (cBot.getHeight() + lvBottom.getDividerHeight()) * totalItemCount
- lvBottom.getDividerHeight() - lvBottom.getHeight();
int lvBotPointer = (cBot.getHeight() + lvBottom.getDividerHeight()) * firstVisibleItem
- cBot.getTop();
int lvTopPointer = (int)((float)(lvBotMovableRange - lvBotPointer)/lvBotMovableRange*lvTopMovableRange);
int setposition = lvTopPointer / (cTop.getHeight() + lvTop.getDividerHeight());
int sety = -lvTopPointer + setposition * (cTop.getHeight() + lvTop.getDividerHeight());
timestampScroll = System.currentTimeMillis();
lvTop.setSelectionFromTop(setposition, sety);
lvBottom.postDelayed(new Runnable() {
#Override
public void run() {
if (System.currentTimeMillis() - timestampScroll > (SCROLL_DELAY - 10)) fInBottomScrollListener = false;
}
}, SCROLL_DELAY);
}
}
};
Hope that helps!
I have a ListView and my each row in the list is a RelativeLayout with an ImageView and TextView. I want to change the dimensions of the RelativeLayout dynamically on scrolling of the list, such that the first and last row width is equal to screen width and the middle row width is half of the screen width. I am able to change the text size of the TextView dynamically using the following code:
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//Calculate height and width based on visible count items and position
final int viewHeight = height // calculated based on row position;
final int viewWidth = width // calculated based on row position;
for (int i = 0; i < visibleItemCount; i++) {
final RelativeLayout layout = (RelativeLayout) listView.getChildAt(i);
final ImageView imageView = (ImageView) layout.findViewById(R.id.icon);
final TextView time = (TextView) layout.findViewById(R.id.time);
time.post(new Runnable() {
#Override
public void run() {
time.setTextSize(viewHeight);
}
});
}
}
But I want to change the dimensions of the entire row layout dynamically on scrolling. Please check my code below. Any help appreciated.
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//Calculate height and width based on visible count items and position
final int viewHeight = height // calculated based on row position;
final int viewWidth = width // calculated based on row position;
for (int i = 0; i < visibleItemCount; i++) {
final RelativeLayout layout = (RelativeLayout) listView.getChildAt(i);
setLayoutDimensions(layout,viewHeight, viewWidth);
}
}
private void setLayoutDimensions(final RelativeLayout layout, final int height, final int width){
RelativeLayout.LayoutParams adaptLayout = new RelativeLayout.LayoutParams(width, height);
layout.setLayoutParams(adaptLayout);
}
Try to set the width and height of the relative layout like this:
LayoutParams params = layout.getLayoutParams();
params.width= width;
params.width= height;
layout.setLayoutParams(params);
you can convert your width and height to dp according to the device screen density using TypedValue.applyDimension method
I have a RelativeLayout with a ListView and a custom View. I set the size of the custom view as the size of the ListView programatically. So I have the custom view on top of the ListView with both the same size. Obviously the ListView is not scrolling because the onTouchListener is on the customView. How can I make both scroll at the same time?
At the end I created a function in my custom view class with the ListView and the number of rows as parameters. Inside the view I setOnScrollListener to the ListView and I assign the position of my ListView while scrolling to my global scroll variable of the custom view. Like this
public void setListView(final TwoWayView listView, int totalListRows) {
totalColumns = totalListRows;
for (int i = 0; i < totalColumns; i++) {
listViewItemHeights.put(i, (int) getResources().getDimension(R.dimen.column_width));
}
listView.setOnScrollListener(new TwoWayView.OnScrollListener() {
#Override
public void onScrollStateChanged(TwoWayView view, int scrollState) {
}
#Override
public void onScroll(TwoWayView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
c = listView.getChildAt(0);
if (c != null) {
int oldScrollX = scrollX;
scrollX = -c.getLeft();
for (int i = 0; i < listView.getFirstVisiblePosition(); ++i) {
if (listViewItemHeights.get(i) != null) {
scrollX += listViewItemHeights.get(i);
}
}
scrollBy(scrollX - oldScrollX, 0);
}
}
});
}
I have a simple ListView inside a ViewPager and a Floating Action Bar at the bottom right corner of the screen.
I want to implement the Quick Return pattern to it so that when I scroll the ListView the FAB goes down the screen and comes up on opposite scroll.
Thanks in advance.
EDIT:
list.setAdapter(new QuickReturnAdapter(adapter));
quickReturnAttacher = QuickReturnAttacher.forView(list);
quickReturnAttacher.addTargetView(floatMenu, AbsListViewScrollTarget.POSITION_BOTTOM, dpToPx(context, 400));
if (quickReturnAttacher instanceof AbsListViewQuickReturnAttacher) {
// This is the correct way to register an OnScrollListener.
// You have to add it on the QuickReturnAttacher, instead
// of on the viewGroup directly.
final AbsListViewQuickReturnAttacher attacher = (AbsListViewQuickReturnAttacher) quickReturnAttacher;
attacher.addOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
Then I am using quickReturnAttacher.setOnItemClickListener and this ain't working.
There is already an open source library for this feature.
https://github.com/felipecsl/QuickReturn
Try this:
final AbsListViewQuickReturnAttacher attacher = (AbsListViewQuickReturnAttacher) quickReturnAttacher;
attacher.addOnScrollListener(this);
attacher.setOnItemClickListener(this);
});
For me this is working, the position passed in the callback is correct.
You have to register your click listener to the attacher, not to the list.
How do you cannot find the method setOnItemClickListener in the class AbsListViewQuickReturnAttacher?
You can try this solution for Quick Return Pattern for ListViews:
yourListView.setOnScrollListener(new AbsListView.OnScrollListener() {
#SuppressLint("NewApi")
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int scrollOffset = 0;
float transitionY;
if (firstVisibleItem > 0) {
scrollOffset += headerHeight;
if (firstVisibleItem > 1) {
scrollOffset += (firstVisibleItem - 1) * cellHeight;
}
}
if (yourListView.getChildCount() > 0) {
scrollOffset += -yourListView.getChildAt(0).getTop();
scrollOffset = -scrollOffset;
}
float scrollDelta = scrollOffset - prevOffset;
float nextY = mQuickReturnView.getY() + scrollDelta;
if (nextY < minRawY) {
transitionY = minRawY;
}
else if (nextY > qReturnDelta) {
transitionY = qReturnDelta;
}
else {
transitionY = nextY;
}
mQuickReturnView.setY(transitionY);
prevOffset = scrollOffset;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
I am trying to achieve view hiding with respect to the scrolling of a listview.
Like in facebook app:-
1.)When listview is scrolled down, the bottom blue layout (Status, Photo, CheckIn) starts to slide up with respect to the amount of listview scrolled and finally becomes visible
2.)When the listview is scrolled up , the bottom layout slides down and finally invisivble.
3.)When the list is scrolled up and down simultaneously, the layout also slides down and up with respect to it
Now, i am trying to achieve it with translation but the result is not quite the same :-
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int currentFirstVisibleItem = view.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
// Scroll down
count = count + 20;
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
// Scroll up
count = count - 20;
}
bottom.setTranslationY(count);
mLastFirstVisibleItem = currentFirstVisibleItem;
}
With this code, the view slides but it is not smooth.
I'm doing something like that in my app. Except that I don't scroll the bottom but the tabhost instead. Maybe you can take out a little of code from here for you.
listView.setOnScrollListener(new AbsListView.OnScrollListener()
{
#Override
public void onScrollStateChanged(AbsListView absListView, int i)
{
if (i == SCROLL_STATE_IDLE)
{
mLastScroll = 0;
if (mTabHost.getTranslationY() >= -mTabHost.getHeight() / 2)
mTabHost.animate().translationY(0).start();
else
mTabHost.animate().translationY(-mTabHost.getHeight()).start();
}
}
#Override
public void onScroll(AbsListView absListView, int i, int i2, int i3)
{
if (absListView.getChildCount() <= 0)
return;
View c = absListView.getChildAt(0);
int scrolly = -c.getTop() + absListView.getFirstVisiblePosition() * c.getHeight();
if (mLastScroll == 0)
mLastScroll = (int)(scrolly + Math.round(mTabHost.getTranslationY() / 0.7));
float dif = 0;
if (mLastScroll - scrolly <= 0)
dif = mLastScroll - scrolly;
else
mLastScroll = scrolly;
if (dif * 0.7 < -mTabHost.getHeight())
{
dif = Math.round(-mTabHost.getHeight() / 0.7);
mLastScroll = (int) Math.round(scrolly - mTabHost.getHeight() / 0.7);
}
mTabHost.setTranslationY(Math.round(dif * 0.7));
}
});