Can't find any information on how to create a rotating ScrollView in Android. With this I mean a ScrollView that restarts when reaching the last element.
I have started to implement my own custom ScrollView that scrolls to the beginning when reaching bottom. But there are still many corner cases that I need to take care off to make it smooth. (Have just put a few minutes on it so far)
public class CardScrollView extends ScrollView {
public CardScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public CardScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CardScrollView(Context context) {
super(context);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if (diff == 0) {
scrollTo(0, -300);
}
super.onScrollChanged(l, t, oldl, oldt);
}
}
I was thinking there should be many people tried doing this before me but can find much information on google. Can anyone point me in the right direction before I spend days into developing my own view?
Turned out a ViewPager is better for my purpose. I found some example code from here.
https://github.com/acbelter/DirectionalCarousel
Related
Write a Custom ListView like:
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
Log.d("onLayout","onLayout=====");
}
}
As I know, when the layout attribute of view has changed, in order to apply the change ( invalide() or requestLayout() ),its parent's onLayout method should be called and layout its children.
So when I scroll the ListView, the layout attribute of its child view has changed, but onLayout doesn't called at all. Why?
Finally I realized relayout a ViewGroup don't need always call onLayout()/layout() method.There are many ways to change views position in ViewGroup,but each way must call onDraw() to write the changed position in FrameBuffer in order to show it in Screen.(Please tell whether I'm wrong with this)
In ListView,I had debug the source code,and when scroll ListView, the stack trace is:
`
trackMotionScroll:5023, AbsListView (android.widget)
scrollIfNeeded:3424, AbsListView (android.widget)
startScrollIfNeeded:3352, AbsListView (android.widget)
onTouchMove:3793, AbsListView (android.widget)
onTouchEvent:3651, AbsListView (android.widget)
dispatchTouchEvent:9294, View (android.view)
in trackMotionScroll,it will call ViewGroup#offsetChildrenTopAndBottom(incrementalDeltaY)
public void offsetChildrenTopAndBottom(int offset) {
final int count = mChildrenCount;
final View[] children = mChildren;
boolean invalidate = false;
for (int i = 0; i < count; i++) {
final View v = children[i];
v.mTop += offset;
v.mBottom += offset;
if (v.mRenderNode != null) {
invalidate = true;
v.mRenderNode.offsetTopAndBottom(offset);
}
}
if (invalidate) {
invalidateViewProperty(false, false);
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
This will cause the reLayout of ListView.
So I got a conclusion,you don't need obey the framework's measure()–layout()–draw() procedure,but only change view's layout attribute and invalidate,it will also change view's layout.
And I guess ListView dispense with layout() when scroll will improve its efficiency
Hello I have a RecyclerView, and I use HorizontalScrollView in children of theRecyclerView`. I need to scroll all of them when I scrolling one. Anyone can tell me How to make that,thanks!
I'm going to assume you're doing something similar to what I did where you have some sort of tabular view and the HorizontalScrollViews are all the same width.
This is how I did it:
First I made a customization to the HorizontalScrollView so I could get event notifications when the view was swiped:
public class HorizontalScrollView extends android.widget.HorizontalScrollView {
private OnScrollListener mListener;
public HorizontalScrollView(Context context) {
super(context);
}
public HorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnScrollListener(OnScrollListener listener) {
mListener = listener;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mListener != null) {
mListener.onScrollChanged(this, l);
}
}
public interface OnScrollListener {
public void onScrollChanged(View view, int scrollX);
}
}
Then when I create the ViewHolders I add a listener that will set all the views to the same scrollX:
view.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollChanged(View scrollView, int scrollX) {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
View child = recyclerView.getChildAt(i);
if (child instanceof HorizontalScrollView && child != scrollView) {
HorizontalScrollView scrollView2 = (HorizontalScrollView) child;
if (scrollView2.getScrollX() != scrollX) {
scrollView2.setScrollX(scrollX);
}
}
}
}
});
This code is just for illustration purposes; don't expect to copy/paste this and have it work.
I'm assuming that your ViewHolder can get a reference to your RecyclerView to access all the current list items.
This code had some problems, when you swiped one view then swiped another view while everything was still moving from the first swipe, things could get out of sync. But this is a basic idea to get you started in a positive direction.
I am creating a custom imageview and I am trying to find the height of the parent. The only detail I know about the parent is that it would potentially scroll. The reason I need the parent's height is because I am trying to figure out the imageview's position on the screen. I have made an equation that works for accurately calculating its position, but it only works when I manually enter in the parents height. Is there any way to retrieve this information or is there another way to get my imageview's position on the screen every time it changes?
Try this way
public class MyImageView extends ImageView {
int parentHeight;
int parentWidth;
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyImageView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(((View)this.getParent()).getMeasuredWidth()!=0){
parentHeight = ((View)this.getParent()).getMeasuredHeight();
parentWidth = ((View)this.getParent()).getMeasuredWidth();
}
}
}
I want to implement an activity which have search functionality that searches contents of a list view.The search bar should be on top of list view and it should hide from user when he scroll down the list view. And when he is searching for something , it should always be on top of list view!!! How can I implement it.
Best Regards!
I assume you know how to create XML layout with SearchView on top of a ListView, it could be placed inside vertical LinearLayout. The tricky part is how to manipulate SearchView, right?
You can register a listener on your ListView like this:
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case SCROLL_STATE_IDLE:
//scroll was stopped, let's show search bar again
break;
case SCROLL_STATE_TOUCH_SCROLL:
//user is scrolling, let's hide search bar
break;
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > 0) {
//user scrolled down, first element is hidden
}
}
});
As you can see, you will be informed about state changes and you can use it if you want hide searchview during touch events. Or, you can listen for changing visible elements. This simple if statement checking firstVisibleItem > 0 will tell you when user scrolled down. You can also track disappearing list items and react whenever any item is shown or hidden.
Another way to listen for scroll changes is extending ListView and override onScrollChanged() method e.g.
class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
for (Callbacks c : mCallbacks) {
c.onScrollChanged(l - oldl, t - oldt);
}
}
#Override
public int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
public void addCallbacks(Callbacks listener) {
if (!mCallbacks.contains(listener)) {
mCallbacks.add(listener);
}
}
public static interface Callbacks {
public void onScrollChanged(int deltaX, int deltaY);
}
}
Google folks did something like it in IOSched app.
Is there a way to determine the current scroll offset or scroll position of a GridView?
View.getScrollY() // Underlying var is not updated during a scroll.
I have tried setting an OnScrollListener but the onScroll callback is not fine grained enough for my purposes.
Here is the how I'm attempting to determine the scroll offset using an OnScrollListener.
private int getScrollY() {
int top = 0;
if (mGridView.getChildCount() > 0) {
final View firstView = mGridView.getChildAt(0);
top = firstView.getTop();
}
return top;
}
The issue with this code is that the returned y offset is inaccurate when scrolling upwards; the top view is recycled and hence, the y offset seems to jump;
Is there a nice way of calculating the scroll offset of a GridView? I can't seem to find a good solution.
Use this.
public class CustomGridView extends GridView {
public CustomGridView(Context context) {
super(context);
}
public CustomGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/* ADD THIS */
#Override
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
}
Returns an int value when called
You can use GridView.getFirstVisiblePosition().
http://developer.android.com/reference/android/widget/AdapterView.html#getFirstVisiblePosition()