I have a RelativeLayout which has a ViewDragHelper which I use to move the view around. I have to use layout(boolean changed, int left, int top, int right, int bottom) to update view size on drag because the views inside need to resize. This all works fine. Now the child views inside have their touch positions all wrong. Buttons work as if they were on top of the screen as opposed to where they are.
private void changeDragViewPosition(int top, float verticalMovementFactor) {
int right = calculateViewRightPosition(verticalMovementFactor);
int left = right - mainViewLayoutParams.width;
int bottom = top + mainViewLayoutParams.height;
mainView.layout(left, top, right, bottom);
}
This is the code I use for moving the view. The mainView itself has correct touch positions, the child views inside this view are the problem. Is there anything I'm missing?
EDIT
Here is the view i'm using.
public class MinimizableView extends RelativeLayout {
private static final int DEFAULT_MINIMIZED_MARGIN = 2;
private static final int DEFAULT_SCALE_FACTOR = 2;
private static final int MIN_SLIDING_DISTANCE_ON_CLICK = 10;
private View mainView;
private View unmovableView;
private ArrayList<View> otherViews;
private ArrayList<Integer> initialPositions;
private LayoutParams mainViewLayoutParams;
private int verticalDragRange;
private int mainViewOriginalWidth;
private int mainViewOriginalHeight;
private float scaleFactor = DEFAULT_SCALE_FACTOR;
private float minimizedMargin;
private boolean firstLayoutPass = true;
private int mainViewInitialPosition;
private float lastTouchActionDownXPosition;
private ViewDragHelper viewDragHelper;
private MinimizableViewListener listener;
public interface MinimizableViewListener {
void onMinimized();
void onMaximized();
void onClosed();
}
public MinimizableView(Context context) {
super(context);
init(context);
}
public MinimizableView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MinimizableView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
minimizedMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MINIMIZED_MARGIN, getResources().getDisplayMetrics());
ViewCompat.requestApplyInsets(this);
}
private ViewDragHelper.Callback viewDragHelperCallback = new ViewDragHelper.Callback() {
private static final int MINIMUM_DX_FOR_HORIZONTAL_DRAG = 5;
private static final int MINIMUM_DY_FOR_VERTICAL_DRAG = 15;
private static final float X_MIN_VELOCITY = 1500;
private static final float Y_MIN_VELOCITY = 1000;
#Override
public boolean tryCaptureView(View child, int pointerId) {
return child.equals(mainView);
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
if (!isMainViewAtBottom()) {
float verticalMovementFactor = (top - mainViewInitialPosition) / (float) verticalDragRange;
changeDragViewScale(verticalMovementFactor);
changeDragViewPosition(top, verticalMovementFactor);
changeSecondViewAlpha(verticalMovementFactor);
changeSecondViewPosition(verticalMovementFactor);
changeUnmovableViewAlpha(verticalMovementFactor);
}
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (isMainViewAtBottom() && !isViewAtRight(releasedChild)) {
triggerOnReleaseActionsWhileHorizontalDrag(xvel);
} else {
triggerOnReleaseActionsWhileVerticalDrag(yvel);
}
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
int newTop = verticalDragRange + mainViewInitialPosition;
if (isMinimized() && Math.abs(dy) >= MINIMUM_DY_FOR_VERTICAL_DRAG || (!isMinimized() && !isMainViewAtBottom())) {
final int topBound = getPaddingTop() + mainViewInitialPosition;
final int bottomBound = verticalDragRange + mainViewInitialPosition;
newTop = Math.min(Math.max(top, topBound), bottomBound);
}
return newTop;
}
#Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int newLeft = mainView.getLeft();
if ((isMinimized() && Math.abs(dx) > MINIMUM_DX_FOR_HORIZONTAL_DRAG) || (isMainViewAtBottom() && !isViewAtRight(mainView))) {
newLeft = left;
}
return newLeft;
}
private void triggerOnReleaseActionsWhileHorizontalDrag(float xvel) {
if (xvel < 0 && xvel <= -X_MIN_VELOCITY) {
closeToLeft();
} else if (xvel > 0 && xvel >= X_MIN_VELOCITY) {
closeToRight();
} else {
if (isNextToLeftBound(mainView)) {
closeToLeft();
} else if (isNextToRightBound(mainView)) {
closeToRight();
} else {
minimize();
}
}
}
private void triggerOnReleaseActionsWhileVerticalDrag(float xvel) {
if (xvel < 0 && xvel <= -Y_MIN_VELOCITY) {
maximize();
} else if (xvel > 0 && xvel >= Y_MIN_VELOCITY) {
minimize();
} else {
if (isDragViewAboveTheMiddle(mainView)) {
maximize();
} else {
minimize();
}
}
}
};
private void changeDragViewScale(float verticalMovementFactor) {
mainViewLayoutParams.width = (int) (mainViewOriginalWidth * (1 - (verticalMovementFactor / scaleFactor)));
mainViewLayoutParams.height = (int) (mainViewOriginalHeight * (1 - (verticalMovementFactor / scaleFactor)));
mainView.setLayoutParams(mainViewLayoutParams);
}
private void changeDragViewPosition(int top, float verticalMovementFactor) {
int right = calculateViewRightPosition(verticalMovementFactor);
int left = right - mainViewLayoutParams.width;
int bottom = top + mainViewLayoutParams.height;
mainView.layout(left, top, right, bottom);
}
private void changeSecondViewAlpha(float verticalMovementFactor) {
for (int i = 0; i < otherViews.size(); i++) {
otherViews.get(i).setAlpha(1 - verticalMovementFactor);
}
}
private void changeSecondViewPosition(float verticalMovementFactor) {
int newTop;
int initialTop;
for (int i = 0; i < otherViews.size(); i++) {
initialTop = initialPositions.get(i);
newTop = (int) (initialTop + ((getBottom() - initialTop) * verticalMovementFactor));
otherViews.get(i).setY(newTop);
}
}
private void changeUnmovableViewAlpha(float verticalMovementFactor) {
unmovableView.setAlpha(1 - verticalMovementFactor);
}
private int calculateViewRightPosition(float verticalMoveFactor) {
return (int) (mainViewOriginalWidth - minimizedMargin * verticalMoveFactor);
}
private boolean isDragViewAboveTheMiddle(View view) {
int parentHeight = getHeight();
float viewYPosition = view.getY() + (view.getHeight() * 0.5f);
return viewYPosition < (parentHeight * 0.5);
}
private boolean isMainViewAtTop() {
return mainView.getTop() == mainViewInitialPosition;
}
private boolean isMainViewAtBottom() {
return mainView.getBottom() >= getBottom() - getPaddingBottom() - minimizedMargin - 1;
}
private boolean isViewAtRight(View view) {
return view.getRight() + minimizedMargin + 10 >= getWidth() - 10;
}
private void closeToLeft() {
if (viewDragHelper.smoothSlideViewTo(mainView, -mainViewOriginalWidth, getHeight() - getMinHeightPlusMargin())) {
ViewCompat.postInvalidateOnAnimation(this);
}
if (listener != null) {
listener.onClosed();
}
}
private int getMinHeightPlusMargin() {
return (int) (mainViewOriginalHeight * (1 - 1 / scaleFactor) + minimizedMargin);
}
private int getMinWidthPlusMargin() {
return (int) (mainViewOriginalWidth * (1 - 1 / scaleFactor) + minimizedMargin);
}
private void closeToRight() {
if (viewDragHelper.smoothSlideViewTo(mainView, mainViewOriginalWidth, getHeight() - getMinHeightPlusMargin())) {
ViewCompat.postInvalidateOnAnimation(this);
}
if (listener != null) {
listener.onClosed();
}
}
private boolean isNextToLeftBound(View view) {
return (view.getLeft() - minimizedMargin) < getWidth() * 0.05;
}
private boolean isNextToRightBound(View view) {
return (view.getLeft() - minimizedMargin) > getWidth() * 0.75;
}
private boolean isViewHit(View view, int x, int y) {
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0]
&& screenX < viewLocation[0] + view.getWidth()
&& screenY >= viewLocation[1]
&& screenY < viewLocation[1] + view.getHeight();
}
private static final int INVALID_POINTER = -1;
private int activePointerId;
#Override
public boolean onTouchEvent(MotionEvent event) {
int actionMasked = MotionEventCompat.getActionMasked(event);
if ((actionMasked & MotionEventCompat.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
activePointerId = MotionEventCompat.getPointerId(event, actionMasked);
}
if (activePointerId == INVALID_POINTER) {
return false;
}
viewDragHelper.processTouchEvent(event);
if (isClosed()) {
return false;
}
boolean isDragViewHit = isViewHit(mainView, (int) event.getX(), (int) event.getY());
boolean isSecondViewHit = false;
for (int i = 0; i < otherViews.size(); i++) {
if (isViewHit(otherViews.get(i), (int) event.getX(), (int) event.getY())) {
isSecondViewHit = true;
break;
}
}
analyzeTouchToMaximizeIfNeeded(event, isDragViewHit);
if (isMaximized()) {
mainView.dispatchTouchEvent(event);
} else {
mainView.dispatchTouchEvent(cloneMotionEventWithAction(event, MotionEvent.ACTION_CANCEL));
}
return isDragViewHit || isSecondViewHit;
}
private void analyzeTouchToMaximizeIfNeeded(MotionEvent ev, boolean isDragViewHit) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchActionDownXPosition = ev.getX();
break;
case MotionEvent.ACTION_UP:
float clickOffset = ev.getX() - lastTouchActionDownXPosition;
if (shouldMaximizeOnClick(ev, clickOffset, isDragViewHit)) {
if (isMinimized()) {
maximize();
}
}
break;
default:
break;
}
}
public boolean shouldMaximizeOnClick(MotionEvent ev, float deltaX, boolean isDragViewHit) {
return (Math.abs(deltaX) < MIN_SLIDING_DISTANCE_ON_CLICK) && ev.getAction() != MotionEvent.ACTION_MOVE && isDragViewHit;
}
private MotionEvent cloneMotionEventWithAction(MotionEvent event, int action) {
return MotionEvent.obtain(event.getDownTime(), event.getEventTime(), action, event.getX(), event.getY(), event.getMetaState());
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isEnabled()) {
return false;
}
switch (MotionEventCompat.getActionMasked(ev) & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
viewDragHelper.cancel();
return false;
case MotionEvent.ACTION_DOWN:
int index = MotionEventCompat.getActionIndex(ev);
activePointerId = MotionEventCompat.getPointerId(ev, index);
if (activePointerId == INVALID_POINTER) {
return false;
}
break;
default:
break;
}
boolean interceptTap = viewDragHelper.isViewUnder(mainView, (int) ev.getX(), (int) ev.getY());
return viewDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
otherViews = new ArrayList<>();
initialPositions = new ArrayList<>();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof DragView) {
mainView = child;
} else if (child instanceof UnmovableView) {
unmovableView = child;
} else {
otherViews.add(child);
}
}
viewDragHelper = ViewDragHelper.create(this, 1, viewDragHelperCallback);
mainViewLayoutParams = (LayoutParams) mainView.getLayoutParams();
}
#Override
public void computeScroll() {
if (!isInEditMode() && viewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (isInEditMode() || firstLayoutPass) {
super.onLayout(changed, left, top, right, bottom);
mainViewInitialPosition = mainView.getTop();
verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < otherViews.size(); i++) {
initialPositions.add(otherViews.get(i).getTop());
}
firstLayoutPass = false;
} else if (isMainViewAtTop()) {
mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight);
changeUnmovableViewAlpha(0);
setLayoutPositions(0, left, right, bottom, true);
} else {
setLayoutPositions(mainView.getTop() / (float) verticalDragRange, left, right, bottom, false);
}
}
private void setLayoutPositions(float offsetFactor, int left, int right, int bottom, boolean setY) {
int newTop;
int initialTop;
for (int i = 0; i < otherViews.size(); i++) {
View view = otherViews.get(i);
initialTop = initialPositions.get(i);
newTop = (int) (initialTop + ((bottom - initialTop) * offsetFactor));
view.layout(left, newTop, right, newTop + view.getHeight());
if (setY) {
view.setY(newTop);
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mainViewOriginalWidth == 0) {
mainViewOriginalWidth = mainView.getMeasuredWidth();
mainViewOriginalHeight = mainView.getMeasuredHeight();
}
}
private boolean smoothSlideTo(float slideOffset) {
final int topBound = mainViewInitialPosition + getPaddingTop();
int x = (int) (slideOffset * (getWidth() - getMinWidthPlusMargin()));
int y = (int) ((slideOffset * verticalDragRange) + topBound);
if (viewDragHelper.smoothSlideViewTo(mainView, x, y)) {
ViewCompat.postInvalidateOnAnimation(this);
return true;
}
return false;
}
#Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
mainViewInitialPosition = mainView.getTop();
verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < otherViews.size(); i++) {
initialPositions.add(otherViews.get(i).getTop());
}
}
// --------- PUBLIC METHODS ---------- //
public void maximize() {
smoothSlideTo(0);
if (listener != null) {
listener.onMaximized();
}
}
public void minimize() {
smoothSlideTo(1);
if (listener != null) {
listener.onMinimized();
}
}
public boolean isMinimized() {
return isMainViewAtBottom() && isViewAtRight(mainView);
}
public boolean isMaximized() {
return mainView.getTop() == mainViewInitialPosition;
}
public boolean isClosed() {
return mainView.getRight() <= 0 && mainView.getLeft() >= getWidth();
}
public void setMinimizableViewListener(MinimizableViewListener listener) {
this.listener = listener;
}
public void hide() {
changeDragViewScale(1);
changeDragViewPosition(verticalDragRange + mainViewInitialPosition - getMinHeightPlusMargin(), 1);
minimize();
setVisibility(GONE);
}
public void show() {
setVisibility(VISIBLE);
post(new Runnable() {
#Override
public void run() {
maximize();
}
});
}
}
As you can see the I have overridden the onTouch method and it works fine. I also have a button inside the mainView which is also a RelativeLayout.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lycatv.dragviewtest.MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/actionBar1"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/actionBar1"
android:fitsSystemWindows="true"
android:text="Hello World!" />
<com.lycatv.dragviewtest.MinimizableView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="56dp">
<com.lycatv.dragviewtest.UnmovableView
android:id="#+id/actionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimaryDark" />
</com.lycatv.dragviewtest.UnmovableView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/actionBar"
android:background="#1a1e39" />
<FrameLayout
android:id="#+id/frameLayout1"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="#+id/actionBar" />
<com.lycatv.dragviewtest.DragView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/frameLayout1">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="#000000" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="CheckBox"
android:textColor="#android:color/white" />
</com.lycatv.dragviewtest.DragView>
</com.lycatv.dragviewtest.MinimizableView>
The CheckBox does not work when you press on it. Instead it works when you press above it. By whatever amount it is offset from the top.
I won't pretend to understand everything your code is doing. However, I did notice something in onLayout that is almost certainly wrong. The code you posted is this:
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (isInEditMode() || firstLayoutPass) {
super.onLayout(changed, left, top, right, bottom);
mainViewInitialPosition = mainView.getTop();
verticalDragRange = getMeasuredHeight() - mainViewInitialPosition - getMinHeightPlusMargin() - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < otherViews.size(); i++) {
initialPositions.add(otherViews.get(i).getTop());
}
firstLayoutPass = false;
} else if (isMainViewAtTop()) {
mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight);
changeUnmovableViewAlpha(0);
setLayoutPositions(0, left, right, bottom, true);
} else {
setLayoutPositions(mainView.getTop() / (float) verticalDragRange, left, right, bottom, false);
}
}
The almost-certain error in this code is in your use of the layout method here:
mainView.layout(left, mainViewInitialPosition, right, mainViewInitialPosition + mainViewOriginalHeight);
You are using the parameter values left and right to layout your mainView (and the child views via setLayoutPositions). This is wrong. Keep in mind that left, top, right and bottom are the relative positions of your parent view (MinimizableView); they should not be used directly to layout your child views, as the child views are relative to MinimizableView, not to the parent of MinimizableView.
For example, when you layout mainView, the values you pass into layout() are interpreted as being relative to MinimizableView. This means the left offset should be 0 (plus padding), not left. Diddo for right. You layout call should look more like:
mainView.layout(0, 0, mainView.getMeasuredWidth(), mainView.getMeasuredHeight());
Note that the above does not account for padding.
Related
I am trying to change viewpage rotation 90 to get vertical swapping. but my adapter going aside. i am unable fix it. help me to fit layout correctly.
Here is my view pager
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_alignParentTop="true"
android:layout_width="800dp"
android:layout_height="480dp"
android:background="#a07fe0">
</android.support.v4.view.ViewPager>
Here is my layout of adapter
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ff0000"
android:layout_centerInParent="true"
android:orientation="horizontal"
android:rotation="270">
<ImageView
android:id="#+id/screen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="start"
android:src="#drawable/makescr1"/>
</LinearLayout>
Use This class.
public class VerticalPager extends ViewGroup {
public static final String TAG = "VerticalPager";
private static final int INVALID_SCREEN = -1;
public static final int SPEC_UNDEFINED = -1;
private static final int TOP = 0;
private static final int BOTTOM = 1;
/**
* The velocity at which a fling gesture will cause us to snap to the next screen
*/
private static final int SNAP_VELOCITY = 1000;
private int pageHeight;
private int measuredHeight;
private boolean mFirstLayout = true;
private int mCurrentPage;
private int mNextPage = INVALID_SCREEN;
private Scroller mScroller;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private int mMaximumVelocity;
private float mLastMotionY;
private float mLastMotionX;
private final static int TOUCH_STATE_REST = 0;
private final static int TOUCH_STATE_SCROLLING = 1;
private int mTouchState = TOUCH_STATE_REST;
private boolean mAllowLongPress;
private Set<OnScrollListener> mListeners = new HashSet<OnScrollListener>();
VerticalPageChange mpageChangeListener;
/**
* Used to inflate the Workspace from XML.
*
* #param context The application's context.
* #param attrs The attribtues set containing the Workspace's customization values.
*/
public VerticalPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VerticalPager(Context context, AttributeSet attrs,
VerticalPageChange mpageChangeListener) {
this(context, attrs, 0);
this.mpageChangeListener=mpageChangeListener;
}
/**
* Used to inflate the Workspace from XML.
*
* #param context The application's context.
* #param attrs The attribtues set containing the Workspace's customization values.
* #param defStyle Unused.
*/
public VerticalPager(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.com_deezapps_widget_HorizontalPager);
//pageHeightSpec = a.getDimensionPixelSize(R.styleable.com_deezapps_widget_HorizontalPager_pageWidth, SPEC_UNDEFINED);
//a.recycle();
init(context);
}
/**
* Initializes various states for this workspace.
*/
private void init(Context context) {
mScroller = new Scroller(getContext(), new DecelerateInterpolator());
mCurrentPage = 0;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
/**
* Returns the index of the currently displayed page.
*
* #return The index of the currently displayed page.
*/
int getCurrentPage() {
return mCurrentPage;
}
/**
* Sets the current page.
*
* #param currentPage
*/
public void setCurrentPage(int currentPage) {
mCurrentPage = Math.max(0, Math.min(currentPage, getChildCount()));
scrollTo(getScrollYForPage(mCurrentPage), 0);
invalidate();
}
public int getPageHeight() {
return pageHeight;
}
// public void setPageHeight(int pageHeight) {
// this.pageHeightSpec = pageHeight;
// }
/**
* Gets the value that getScrollX() should return if the specified page is the current page (and no other scrolling is occurring).
* Use this to pass a value to scrollTo(), for example.
* #param whichPage
* #return
*/
private int getScrollYForPage(int whichPage) {
int height = 0;
for(int i = 0; i < whichPage; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
height += child.getHeight();
}
}
return height - pageHeightPadding();
}
#Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
} else if (mNextPage != INVALID_SCREEN) {
mCurrentPage = mNextPage;
mNextPage = INVALID_SCREEN;
clearChildrenCache();
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
// ViewGroup.dispatchDraw() supports many features we don't need:
// clip to padding, layout animation, animation listener, disappearing
// children, etc. The following implementation attempts to fast-track
// the drawing dispatch by drawing only what we know needs to be drawn.
final long drawingTime = getDrawingTime();
// todo be smarter about which children need drawing
final int count = getChildCount();
for (int i = 0; i < count; i++) {
drawChild(canvas, getChildAt(i), drawingTime);
}
for (OnScrollListener mListener : mListeners) {
int adjustedScrollY = getScrollY() + pageHeightPadding();
mListener.onScroll(adjustedScrollY);
if (adjustedScrollY % pageHeight == 0) {
mListener.onViewScrollFinished(adjustedScrollY / pageHeight);
}
}
}
int pageHeightPadding() {
return ((getMeasuredHeight() - pageHeight) / 2);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
pageHeight = getMeasuredHeight();
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(pageHeight, MeasureSpec.UNSPECIFIED));
}
if (mFirstLayout) {
scrollTo(getScrollYForPage(mCurrentPage), 0);
mFirstLayout = false;
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
measuredHeight = 0;
final int count = getChildCount();
int height;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
if(i == 0) {
child.getHeight();
child.layout(0, measuredHeight, right - left, measuredHeight + (int)(pageHeight*.96));
measuredHeight += (pageHeight*.96);
} else {
height = pageHeight * (int)Math.ceil((double)child.getMeasuredHeight()/(double)pageHeight);
height = Math.max(pageHeight, height);
child.layout(0, measuredHeight, right - left, measuredHeight + height);
measuredHeight += height;
}
}
}
}
#Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int screen = indexOfChild(child);
if (screen != mCurrentPage || !mScroller.isFinished()) {
return true;
}
return false;
}
#Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
int focusableScreen;
try
{
if (mNextPage != INVALID_SCREEN) {
focusableScreen = mNextPage;
} else {
focusableScreen = mCurrentPage;
}
getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
}
catch(Exception e)
{
e.printStackTrace();
}
return false;
}
#Override
public boolean dispatchUnhandledMove(View focused, int direction) {
if (direction == View.FOCUS_LEFT) {
if (getCurrentPage() > 0) {
snapToPage(getCurrentPage() - 1);
return true;
}
} else if (direction == View.FOCUS_RIGHT) {
if (getCurrentPage() < getChildCount() - 1) {
snapToPage(getCurrentPage() + 1);
return true;
}
}
return super.dispatchUnhandledMove(focused, direction);
}
#Override
public void addFocusables(ArrayList<View> views, int direction) {
getChildAt(mCurrentPage).addFocusables(views, direction);
if (direction == View.FOCUS_LEFT) {
if (mCurrentPage > 0) {
getChildAt(mCurrentPage - 1).addFocusables(views, direction);
}
} else if (direction == View.FOCUS_RIGHT){
if (mCurrentPage < getChildCount() - 1) {
getChildAt(mCurrentPage + 1).addFocusables(views, direction);
}
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//Log.d(TAG, "onInterceptTouchEvent::action=" + ev.getAction());
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
/*
* Shortcut the most recurring case: the user is in the dragging
* state and he is moving his finger. We want to intercept this
* motion.
*/
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
//Log.d(TAG, "onInterceptTouchEvent::shortcut=true");
return true;
}
final float y = ev.getY();
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_MOVE:
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
if (mTouchState == TOUCH_STATE_REST) {
checkStartScroll(x, y);
}
break;
case MotionEvent.ACTION_DOWN:
// Remember location of down touch
mLastMotionX = x;
mLastMotionY = y;
mAllowLongPress = true;
/*
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
*/
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Release the drag
clearChildrenCache();
mTouchState = TOUCH_STATE_REST;
break;
}
/*
* The only time we want to intercept motion events is if we are in the
* drag mode.
*/
return mTouchState != TOUCH_STATE_REST;
}
private void checkStartScroll(float x, float y) {
/*
* Locally do absolute value. mLastMotionX is set to the y value
* of the down event.
*/
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
boolean xMoved = xDiff > mTouchSlop;
boolean yMoved = yDiff > mTouchSlop;
if (xMoved || yMoved) {
if (yMoved) {
// Scroll if the user moved far enough along the X axis
mTouchState = TOUCH_STATE_SCROLLING;
enableChildrenCache();
}
// Either way, cancel any pending longpress
if (mAllowLongPress) {
mAllowLongPress = false;
// Try canceling the long press. It could also have been scheduled
// by a distant descendant, so use the mAllowLongPress flag to block
// everything
final View currentScreen = getChildAt(mCurrentPage);
currentScreen.cancelLongPress();
}
}
}
void enableChildrenCache() {
setChildrenDrawingCacheEnabled(true);
setChildrenDrawnWithCacheEnabled(true);
}
void clearChildrenCache() {
setChildrenDrawnWithCacheEnabled(false);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
if (mTouchState == TOUCH_STATE_REST) {
checkStartScroll(y, x);
} else if (mTouchState == TOUCH_STATE_SCROLLING) {
// Scroll to follow the motion event
int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
// Apply friction to scrolling past boundaries.
final int count = getChildCount();
if (getScrollY() < 0 || getScrollY() + pageHeight > getChildAt(count - 1).getBottom()) {
deltaY /= 2;
}
scrollBy(0, deltaY);
}
break;
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int velocityY = (int) velocityTracker.getYVelocity();
final int count = getChildCount();
// check scrolling past first or last page?
if(getScrollY() < 0) {
snapToPage(0);
} else if(getScrollY() > measuredHeight - pageHeight) {
snapToPage(count - 1, BOTTOM);
} else {
for(int i = 0; i < count; i++) {
final View child = getChildAt(i);
if(child.getTop() < getScrollY() &&
child.getBottom() > getScrollY() + pageHeight) {
// we're inside a page, fling that bitch
mNextPage = i;
mScroller.fling(getScrollX(), getScrollY(), 0, -velocityY, 0, 0, child.getTop(), child.getBottom() - getHeight());
invalidate();
break;
} else if(child.getBottom() > getScrollY() && child.getBottom() < getScrollY() + getHeight()) {
// stuck in between pages, oh snap!
if(velocityY < -SNAP_VELOCITY) {
snapToPage(i + 1);
} else if(velocityY > SNAP_VELOCITY) {
snapToPage(i, BOTTOM);
} else if(getScrollY() + pageHeight/2 > child.getBottom()) {
snapToPage(i + 1);
} else {
snapToPage(i, BOTTOM);
}
break;
}
}
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
}
return true;
}
private void snapToPage(final int whichPage, final int where) {
enableChildrenCache();
boolean changingPages = whichPage != mCurrentPage;
mNextPage = whichPage;
View focusedChild = getFocusedChild();
if (focusedChild != null && changingPages && focusedChild == getChildAt(mCurrentPage)) {
focusedChild.clearFocus();
}
final int delta;
if(getChildAt(whichPage).getHeight() <= pageHeight || where == TOP) {
delta = getChildAt(whichPage).getTop() - getScrollY();
} else {
delta = getChildAt(whichPage).getBottom() - pageHeight - getScrollY();
}
mScroller.startScroll(0, getScrollY(), 0, delta, 400);
invalidate();
mpageChangeListener.onPageChange(whichPage);
}
public void snapToPage(final int whichPage) {
snapToPage(whichPage, TOP);
// mpageChangeListener.onPageChange(whichPage);
}
#Override
protected Parcelable onSaveInstanceState() {
final SavedState state = new SavedState(super.onSaveInstanceState());
state.currentScreen = mCurrentPage;
return state;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
if (savedState.currentScreen != INVALID_SCREEN) {
mCurrentPage = savedState.currentScreen;
}
}
public void scrollUp() {
if (mNextPage == INVALID_SCREEN && mCurrentPage > 0 && mScroller.isFinished()) {
snapToPage(mCurrentPage - 1);
}
}
public void scrollDown() {
if (mNextPage == INVALID_SCREEN && mCurrentPage < getChildCount() - 1 && mScroller.isFinished()) {
snapToPage(mCurrentPage + 1);
}
}
public int getScreenForView(View v) {
int result = -1;
if (v != null) {
ViewParent vp = v.getParent();
int count = getChildCount();
for (int i = 0; i < count; i++) {
if (vp == getChildAt(i)) {
return i;
}
}
}
return result;
}
/**
* #return True is long presses are still allowed for the current touch
*/
public boolean allowLongPress() {
return mAllowLongPress;
}
public static class SavedState extends BaseSavedState {
int currentScreen = -1;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
currentScreen = in.readInt();
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(currentScreen);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
public void addOnScrollListener(OnScrollListener listener) {
mListeners.add(listener);
}
public void removeOnScrollListener(OnScrollListener listener) {
mListeners.remove(listener);
}
/**
* Implement to receive events on scroll position and page snaps.
*/
public static interface OnScrollListener {
/**
* Receives the current scroll X value. This value will be adjusted to assume the left edge of the first
* page has a scroll position of 0. Note that values less than 0 and greater than the right edge of the
* last page are possible due to touch events scrolling beyond the edges.
* #param scrollX Scroll X value
*/
void onScroll(int scrollX);
/**
* Invoked when scrolling is finished (settled on a page, centered).
* #param currentPage The current page
*/
void onViewScrollFinished(int currentPage);
}
public interface VerticalPageChange
{
public void onPageChange(int position);
}
}
i have add page dynamically. inside getView() of BaseAdapter.
final VerticalPager verticalPage = new VerticalPager(Act_ItemView.this, null, mVerticalPagechangeListener);
// setDotIndicatorView(p.arr_images.size());
for (int i = 0; i < p.arr_images.size(); i++)
{
final String image_url = p.arr_images.get(i).image_method;
PhotoView imageView = new PhotoView(Act_ItemView.this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
imageView.setAllowParentInterceptOnEdge(true);
// imageView.setScaleType(ScaleType.CENTER_CROP);
imageLoader1.displayImage(image_url, imageView, options1, animateFirstListener1);
verticalPage.setHorizontalScrollBarEnabled(false);
verticalPage.addView(imageView);
verticalPage.setTag(i);
}
linearLayoutMain.addView(verticalPage);
here is listner implemented
VerticalPager.VerticalPageChange mVerticalPagechangeListener = new VerticalPager.VerticalPageChange()
{
#Override
public void onPageChange(int position)
{
try
{
int pagePosition1 = viewPager.getCurrentItem();
setindicator(position);
array_index[pagePosition1] = position;
} catch (Exception e)
{
e.printStackTrace();
}
}
};
How to change background color when clicking on Horizontal Listview items in Android
So that users will know where they clicked.
Below is the code which I got from some internet site.
HorizontalListViewActivity.java
public class HorizontalListViewActivity extends Activity {
static BitmapFactory bf;
ArrayList<String> dataObjectsList = new ArrayList<String>();
ArrayList<Bitmap> myImageList = new ArrayList<Bitmap>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HorizontalListView listview = (HorizontalListView) findViewById(R.id.listview);
listview.setAdapter(mAdapter);
dataObjectsList.add("Text #1");
dataObjectsList.add("Text #2");
dataObjectsList.add("Text #3");
dataObjectsList.add("Text #4");
myImageList.add(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/Restaurent/app1.jpg"));
myImageList.add(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/Restaurent/app2.jpg"));
myImageList.add(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/Restaurent/app3.jpg"));
myImageList.add(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/Restaurent/app4.jpg"));
listview.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
/*Toast.makeText(getApplicationContext(), "dataObjects[position]=="+dataObjectsList.get(arg2).toString(), 100).show();
Intent intent2 = new Intent(getApplicationContext(),Test.class);
intent2.putExtra("category", dataObjectsList.get(arg2).toString());
startActivity(intent2);*/
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
private static String[] dataObjects = new String[]{ "Delhi",
"Bangalore",
"Chennai",
"Pune" };
private static Bitmap[] myImage = new Bitmap[]{ BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/Restaurent/app1.jpg"),
BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/City/app2.jpg"),
BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/City/app3.jpg"),
BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath()+"/City/app4.jpg") };
private BaseAdapter mAdapter = new BaseAdapter() {
#Override
public int getCount() {
return dataObjects.length;
}
#Override
public Object getItem(int position) {
Toast.makeText(getApplicationContext(), "getItem 111111 position=="+position, 100).show();
return null;
}
#Override
public long getItemId(int position) {
Toast.makeText(getApplicationContext(), "getItemId position=="+position, 100).show();
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewitem, null);
TextView title = (TextView) retval.findViewById(R.id.title);
ImageView img=(ImageView) retval.findViewById(R.id.image);
img.setImageBitmap(myImageList.get(position));
title.setText(dataObjectsList.get(position).toString());
return retval;
}
};
}
HorizontalListView.java
public class HorizontalListView extends AdapterView<ListAdapter> {
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}
#Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}
#Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
mOnItemClicked = listener;
}
#Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}
private DataSetObserver mDataObserver = new DataSetObserver() {
#Override
public void onChanged() {
synchronized(HorizontalListView.this){
mDataChanged = true;
}
setEmptyView(getEmptyView());
invalidate();
requestLayout();
}
#Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
#Override
public ListAdapter getAdapter() {
return mAdapter;
}
#Override
public View getSelectedView() {
//TODO: implement
return null;
}
#Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}
private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}
#Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
#Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mAdapter == null){
return;
}
if(mDataChanged){
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}
if(mScroller.computeScrollOffset()){
int scrollx = mScroller.getCurrX();
mNextX = scrollx;
}
if(mNextX <= 0){
mNextX = 0;
mScroller.forceFinished(true);
}
if(mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}
int dx = mCurrentX - mNextX;
removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);
mCurrentX = mNextX;
if(!mScroller.isFinished()){
post(new Runnable(){
#Override
public void run() {
requestLayout();
}
});
}
}
private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);
edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}
private void fillListRight(int rightEdge, final int dx) {
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();
if(mRightViewIndex == mAdapter.getCount()-1) {
mMaxX = mCurrentX + rightEdge - getWidth();
}
if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}
}
private void fillListLeft(int leftEdge, final int dx) {
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex--;
mDisplayOffset -= child.getMeasuredWidth();
}
}
private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while(child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex--;
child = getChildAt(getChildCount()-1);
}
}
private void positionItems(final int dx) {
if(getChildCount() > 0){
mDisplayOffset += dx;
int left = mDisplayOffset;
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
left += childWidth;
}
}
}
public synchronized void scrollTo(int x) {
mScroller.startScroll(mNextX, 0, x - mNextX, 0);
requestLayout();
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = mGesture.onTouchEvent(ev);
return handled;
}
protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
synchronized(HorizontalListView.this){
mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
}
requestLayout();
return true;
}
protected boolean onDown(MotionEvent e) {
mScroller.forceFinished(true);
return true;
}
private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDown(MotionEvent e) {
return HorizontalListView.this.onDown(e);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
getParent().requestDisallowInterceptTouchEvent(true);
synchronized(HorizontalListView.this){
mNextX += (int)distanceX;
}
requestLayout();
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Rect viewRect = new Rect();
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
int left = child.getLeft();
int right = child.getRight();
int top = child.getTop();
int bottom = child.getBottom();
viewRect.set(left, top, right, bottom);
if(viewRect.contains((int)e.getX(), (int)e.getY())){
if(mOnItemClicked != null){
mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
if(mOnItemSelected != null){
mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
break;
}
}
return true;
}
#Override
public void onLongPress(MotionEvent e) {
Rect viewRect = new Rect();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int left = child.getLeft();
int right = child.getRight();
int top = child.getTop();
int bottom = child.getBottom();
viewRect.set(left, top, right, bottom);
if (viewRect.contains((int) e.getX(), (int) e.getY())) {
if (mOnItemLongClicked != null) {
mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
break;
}
}
}
};
}
viewitem.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#fff">
<ImageView
android:id="#+id/image"
android:layout_width="145dp"
android:layout_height="145dp"
android:layout_marginTop="5dp"
android:scaleType="centerCrop"
android:src="#drawable/ic_launcher"
/>
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000"
android:text="text"
android:gravity="center_horizontal"
/>
</LinearLayout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fff"
android:orientation="vertical" >
<com.horilistview.HorizontalListView
android:id="#+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ddd" />
</LinearLayout>
For example in this implementation you can use listSelector property in xml. Check out maybe yours also supports similar property or use the one I linked above.
public View selectedRow;//declare this in class
Add this code in onitemSelectListner
if (selectedRow != null) {
selectedRow.setBackgroundResource(color.transparent);
}
selectedRow = view; //view is the onitemSelectListner View
view.setBackgroundResource(R.drawable.list_background);
this will work 3.0 and above version..
add this onclick listener in getview method..
list.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
for(int j=0;j<20;j++)
{
test[j]=0;
}
for (int k = 0; k < mAdapter.getCount(); k++) {
View item = list.getChildAt(k);
if (item != null) {
test[k]=0;
item.setBackgroundColor(Color.WHITE);
}
view.setBackgroundColor(Color.BLUE);
test[i]=1;
}
}
test is a dummy array, use it out of onclicklistner to keep the selected even when view is changed.. like this..
if (test[position]==1) {
relativeLayout.setBackgroundColor(Color.BLUE);
}
if(test[position]==0)
relativeLayout.setBackgroundColor(Color.WHITE);
Im using #Vikram hack found here to mimic the parallax effect solemnly on the header, just like the new Airbnb and Spotify impementations.
The code is working fine on my test app, however on the main app where image size is set programmatically the effect seems to almost disappear, i.e. the image barely changes position/scrolls.
parallax_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<cheesebaron.parallaxscrollview.ParallaxScrollView
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_canvas_parallax_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/parallax_headline"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#ff00aa"
android:scaleType="fitXY" />
<cheesebaron.parallaxscrollview.ObservableLinearLayout
android:id="#+id/observable_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/list_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="300dp"
android:orientation="vertical">
<LinearLayout
android:id="#+id/parallax_list_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</cheesebaron.parallaxscrollview.ObservableLinearLayout>
</cheesebaron.parallaxscrollview.ParallaxScrollView>
ObservableLinearLayout / AnotherView:
public class ObservableLinearLayout : LinearLayout
{
protected ObservableLinearLayout(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public ObservableLinearLayout(Context context)
: base(context)
{
}
public ObservableLinearLayout(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
private IScrollCallbacks mCallbacks;
public interface IScrollCallbacks
{
void OnScrollChanged(int l, int t, int oldl, int oldt);
}
public void SetCallbacks(IScrollCallbacks listener)
{
mCallbacks = listener;
}
//#Override
public override void Draw(Canvas canvas)
{
base.Draw(canvas);
}
//#Override
protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
{
base.OnScrollChanged(l, t, oldl, oldt);
if (mCallbacks != null)
{
mCallbacks.OnScrollChanged(l, t, oldl, oldt);
}
}
//#override
protected override int ComputeVerticalScrollRange()
{
return base.ComputeVerticalScrollRange();
}
}
ParallaxScrollView.cs:
public class ParallaxScrollView : ViewGroup
{
private const GravityFlags DefaultChildGravity = GravityFlags.CenterHorizontal;
private new const string Tag = "ParallaxScrollView";
//private const float DefaultParallaxOffset = 0.2f;
private const float DefaultParallaxOffset = 0.5f;
private float _parallaxOffset = DefaultParallaxOffset;
private View _background;
private ObservableScrollView _scrollView;
private int _backgroundRight;
private int _backgroundBottom;
private int _scrollContentHeight;
private int _scrollViewHeight;
private float _scrollDiff;
public float ParallaxOffset
{
get { return _parallaxOffset; }
set
{
var offset = (float)Math.Round(value * 100) / 100;
_parallaxOffset = offset > 0.0 ? offset : DefaultParallaxOffset;
RequestLayout();
}
}
public ParallaxScrollView(Context context)
: this(context, null)
{ }
public ParallaxScrollView(Context context, IAttributeSet attrs)
: this(context, attrs, 0)
{ }
public ParallaxScrollView(Context context, IAttributeSet attrs, int defStyle)
: base(context, attrs, defStyle)
{
var a = Context.Theme.ObtainStyledAttributes(attrs, Resource.Styleable.parallaxscrollview, 0, 0);
try
{
ParallaxOffset = a.GetFloat(Resource.Styleable.parallaxscrollview_parallaxOffset,
DefaultParallaxOffset);
}
finally
{
a.Recycle();
}
}
public override void AddView(View child)
{
if (ChildCount > 1)
throw new ArgumentException("ParallaxScrollView can only host two direct children");
base.AddView(child);
}
public override void AddView(View child, int index)
{
if (ChildCount > 1)
throw new ArgumentException("ParallaxScrollView can only host two direct children");
base.AddView(child, index);
}
public override void AddView(View child, int index, LayoutParams #params)
{
if (ChildCount > 1)
throw new ArgumentException("ParallaxScrollView can only host two direct children");
base.AddView(child, index, #params);
}
public override void AddView(View child, int width, int height)
{
if (ChildCount > 1)
throw new ArgumentException("ParallaxScrollView can only host two direct children");
base.AddView(child, width, height);
}
public override void AddView(View child, LayoutParams #params)
{
if (ChildCount > 1)
throw new ArgumentException("ParallaxScrollView can only host two direct children");
base.AddView(child, #params);
}
protected override void OnFinishInflate()
{
base.OnFinishInflate();
if (ChildCount > 2)
throw new InvalidOperationException("ParallaxScrollView can only host two direct children");
OrganiseViews();
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
if (_scrollView != null)
{
MeasureChild(_scrollView, MeasureSpec.MakeMeasureSpec(
MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.AtMost),
MeasureSpec.MakeMeasureSpec(MeasureSpec.GetSize(heightMeasureSpec),
MeasureSpecMode.AtMost));
_scrollContentHeight = _scrollView.GetChildAt(0).MeasuredHeight;
_scrollViewHeight = _scrollView.MeasuredHeight;
}
if (_background != null)
{
var minHeight = (int)(_scrollViewHeight + _parallaxOffset
* (_scrollContentHeight - _scrollViewHeight));
minHeight = Math.Max(minHeight, MeasureSpec.GetSize(heightMeasureSpec));
MeasureChild(_background, MeasureSpec.MakeMeasureSpec(
MeasureSpec.GetSize(widthMeasureSpec), MeasureSpecMode.Exactly),
MeasureSpec.MakeMeasureSpec(minHeight, MeasureSpecMode.Exactly));
_backgroundRight = Left + _background.MeasuredWidth;
_backgroundBottom = Top + _background.MeasuredHeight;
_scrollDiff = (_background.MeasuredHeight - _scrollViewHeight)
/ (float)(_scrollContentHeight - _scrollViewHeight);
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
var parentLeft = PaddingLeft;
var parentRight = r - l - PaddingRight;
var parentTop = PaddingTop;
var parentBottom = b - t - PaddingBottom;
if (_scrollView != null && _scrollView.Visibility != ViewStates.Gone)
{
var lp = (FrameLayout.LayoutParams)_scrollView.LayoutParameters;
var width = _scrollView.MeasuredWidth;
var height = _scrollView.MeasuredHeight;
int childLeft;
int childTop;
var gravity = lp.Gravity;
if (gravity == GravityFlags.NoGravity)
gravity = DefaultChildGravity;
var horizontalGravity = gravity & GravityFlags.HorizontalGravityMask;
var verticalGravity = gravity & GravityFlags.VerticalGravityMask;
switch (horizontalGravity)
{
case GravityFlags.Left:
childLeft = parentLeft - lp.LeftMargin;
break;
case GravityFlags.CenterHorizontal:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.LeftMargin - lp.RightMargin;
break;
case GravityFlags.Right:
childLeft = parentRight - width - lp.RightMargin;
break;
default:
childLeft = parentLeft + lp.LeftMargin;
break;
}
switch (verticalGravity)
{
case GravityFlags.Top:
childTop = parentTop + lp.TopMargin;
break;
case GravityFlags.CenterVertical:
childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.TopMargin - lp.BottomMargin;
break;
case GravityFlags.Bottom:
childTop = parentBottom - height - lp.BottomMargin;
break;
default:
childTop = parentTop + lp.TopMargin;
break;
}
_scrollView.Layout(childLeft, childTop, childLeft + width, childTop + height);
}
if (_background != null && _scrollView != null)
{
//var scrollYCenterOffset = -_scrollView.ScrollY;
var scrollYCenterOffset = _scrollView.ScrollY;
//changes the velocity of the effect
var offset = (int)((scrollYCenterOffset * _scrollDiff) / 2);
_background.Layout(Left, offset, _backgroundRight, offset + _backgroundBottom);
}
}
public override LayoutParams GenerateLayoutParams(IAttributeSet attrs)
{
return new FrameLayout.LayoutParams(Context, attrs);
}
protected override LayoutParams GenerateLayoutParams(LayoutParams p)
{
return new FrameLayout.LayoutParams(p);
}
protected override LayoutParams GenerateDefaultLayoutParams()
{
return new FrameLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent,
GravityFlags.CenterHorizontal);
}
protected override bool CheckLayoutParams(LayoutParams p)
{
return p is FrameLayout.LayoutParams;
}
private void OrganiseViews()
{
if (ChildCount <= 0) return;
if (ChildCount == 1)
{
var foreground = GetChildAt(0);
OrganiseBackgroundView(null);
OrganiseForegroundView(foreground);
}
else if (ChildCount == 2)
{
var background = GetChildAt(0);
var foreground = GetChildAt(1);
OrganiseBackgroundView(background);
OrganiseForegroundView(foreground);
}
else
throw new InvalidOperationException("ParallaxScrollView can host only two direct children");
}
private void OrganiseBackgroundView(View background)
{
_background = background;
}
private void OrganiseForegroundView(View foreground)
{
var insertPos = ChildCount - 1;
if (foreground is ObservableScrollView)
{
_scrollView = (ObservableScrollView)foreground;
_scrollView.ScrollChanged -= ScrollViewOnScrollChanged;
}
else if (foreground is ViewGroup && !(foreground is ScrollView))
{
_scrollView = new ObservableScrollView(Context, null);
RemoveView(foreground);
_scrollView.AddView(foreground);
AddView(_scrollView, insertPos);
}
else if (foreground is ScrollView)
{
var child = ((ScrollView)foreground).ChildCount > 0 ?
((ScrollView)foreground).GetChildAt(0) : null;
_scrollView = new ObservableScrollView(Context, null);
RemoveView(foreground);
if (child != null)
_scrollView.AddView(child);
AddView(_scrollView, insertPos);
}
else
{
_scrollView = new ObservableScrollView(Context, null);
RemoveView(foreground);
_scrollView.AddView(foreground);
AddView(_scrollView, insertPos);
}
if (_scrollView != null)
{
_scrollView.LayoutParameters = foreground.LayoutParameters;
_scrollView.ScrollChanged += ScrollViewOnScrollChanged;
_scrollView.FillViewport = true;
}
}
private void ScrollViewOnScrollChanged(object sender, ObservableScrollViewEventArgs args)
{
RequestLayout();
}
}
}
the calling func:
private void BuildMobileListView()
{
var listContainer = FindViewById<LinearLayout>(Resource.Id.parallax_list_holder);
var listMainHolder = FindViewById<LinearLayout>(Resource.Id.list_content_container);
var listView = new MvxListView(listContainer.Context, null, new MainCanvasListAdapter(this, (IMvxAndroidBindingContext) BindingContext))
{
VerticalFadingEdgeEnabled = false,
//the first item is the headline you get it separately
ItemsSource = ViewModel.Items.GetRange(1, ViewModel.Items.Count-1),
LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.FillParent),
CacheColorHint = Color.Transparent,
};
listView.VerticalFadingEdgeEnabled = false;
listView.Divider = null;
listContainer.AddView(listView);
listView.Post(() =>
{
SetScrollableViewHeight(listView, listContainer);
var p = (LinearLayout.LayoutParams)listMainHolder.LayoutParameters;
listMainHolder.LayoutParameters = p;
});
}
the post function:
public void SetScrollableViewHeight(GridView gridView, LinearLayout llMain)
{
var gridViewAdapter = gridView.Adapter;
if (gridViewAdapter == null)
{
return;
}
var totalHeight = 0;
var firstHeight = 0;
var desiredWidth = View.MeasureSpec.MakeMeasureSpec(
gridView.Width, MeasureSpecMode.AtMost);
for (int i = 0; i < gridViewAdapter.Count; i++)
{
if (i == 0)
{
View listItem = gridViewAdapter.GetView(i, null, gridView);
listItem.Measure(desiredWidth, (int)MeasureSpecMode.Unspecified);
firstHeight = listItem.MeasuredHeight + DroidAttributes.MOBILLE_GRID_VIEW_MARGIN*2;
}
totalHeight += firstHeight;
}
totalHeight = totalHeight / 2;
var lParams = (LinearLayout.LayoutParams)llMain.LayoutParameters;
lParams.Height = totalHeight /*+ DroidAttributes.MOBILLE_GRID_VIEW_MARGIN * 2*//*+ (gridView.ListPaddingBottom * (gridViewAdapter.Count - 1)/2)*/;
llMain.LayoutParameters = lParams;
_mobileLayoutContainer.RequestLayout();
}
the margin set on the #+id/list_content_container is set programmatically according to the headline image's width/height.
Ive been using almost the same code on a different project and got much better "parallax" results, can anyone point me to what
I'm trying to add a HorizontalListView (http://www.dev-smart.com/archives/34) to a ListView. The ListView uses an adapter to dynamically attach new HorizontalListView rows for continuous vertical scrolling.
It almost works: the problem is that when the HorizontalListView is scrolled, the ListView will automatically scroll up or down and place the HorizontalListView at the top or bottom edge of the viewport, depending on which it's closest to. How do I stop the HorizontalListView row from snapping into place on scroll?
I used the HorizontalListView as a reference for my "VerticalScrollView":
public class VerticalScrollView extends ListView {
public static interface ScrollListener {
void onScrollEnd();
}
public VerticalScrollView(Context context) {
super(context);
initView();
}
public VerticalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mTopViewIndex = -1;
mBottomViewIndex = 0;
mDisplayOffset = 0;
mCurrentY = 0;
mNextY = 0;
mMaxY = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
setFadingEdgeLength(0);
setItemsCanFocus(false);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mXDistance = mYDistance = 0f;
mLastX = ev.getX();
mLastY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();
mXDistance += Math.abs(curX - mLastX);
mYDistance += Math.abs(curY - mLastY);
mLastX = curX;
mLastY = curY;
if(mXDistance > mYDistance) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
#Override
public ListAdapter getAdapter() {
return mAdapter;
}
#Override
public View getSelectedView() {
//TODO: implement
return null;
}
#Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
super.setAdapter(adapter);
}
private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}
#Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
#Override
protected synchronized void onLayout(
boolean changed,
int left,
int top,
int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mAdapter == null){
return;
}
if(mDataChanged){
int oldCurrentY = mCurrentY;
initView();
removeAllViewsInLayout();
mNextY = oldCurrentY;
mDataChanged = false;
}
if(mScroller.computeScrollOffset()){
int scrollY = mScroller.getCurrY();
mNextY = scrollY;
}
if(mNextY <= 0){
mNextY = 0;
mScroller.forceFinished(true);
}
if(mNextY >= mMaxY) {
mNextY = mMaxY;
mScroller.forceFinished(true);
}
int dy = mCurrentY - mNextY;
removeNonVisibleItems(dy);
fillList(dy);
positionItems(dy);
mCurrentY = mNextY;
if(!mScroller.isFinished()){
post(mRequestLayoutRunnable);
}
}
private void fillList(final int dy) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getBottom();
}
fillListBottom(edge, dy);
edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getTop();
}
fillListTop(edge, dy);
}
private void fillListBottom(int bottomEdge, final int dy) {
while(bottomEdge + dy < getHeight() && mBottomViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mBottomViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
bottomEdge += child.getMeasuredHeight();
if(mBottomViewIndex == mAdapter.getCount()-1) {
mMaxY = mCurrentY + bottomEdge - getHeight();
}
if (mMaxY < 0) {
mMaxY = 0;
}
mBottomViewIndex++;
}
}
private void fillListTop(int topEdge, final int dy) {
while(topEdge + dy > 0 && mTopViewIndex >= 0) {
View child = mAdapter.getView(mTopViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
topEdge -= child.getMeasuredHeight();
mTopViewIndex--;
mDisplayOffset -= child.getMeasuredHeight();
}
}
private void removeNonVisibleItems(final int dy) {
View child = getChildAt(0);
while(child != null && child.getBottom() + dy <= 0) {
mDisplayOffset += child.getMeasuredHeight();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mTopViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dy >= getHeight()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mBottomViewIndex--;
child = getChildAt(getChildCount()-1);
}
}
private void positionItems(final int dy) {
if(getChildCount() > 0){
mDisplayOffset += dy;
int top = mDisplayOffset;
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
int childHeight = child.getMeasuredHeight();
child.layout(0, top, child.getMeasuredWidth(), top + childHeight);
top += childHeight + child.getPaddingBottom();
}
}
}
public synchronized void scrollTo(int y) {
mScroller.startScroll(0, mNextY, 0, y - mNextY);
requestLayout();
}
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mTopViewIndex = -1;
private int mBottomViewIndex = 0;
protected int mCurrentY;
protected int mNextY;
private int mMaxY = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
private boolean mDataChanged = false;
private float mXDistance;
private float mYDistance;
private float mLastX;
private float mLastY;
private Runnable mRequestLayoutRunnable = new Runnable(){
#Override
public void run() {
requestLayout();
}
};
private DataSetObserver mDataObserver = new DataSetObserver() {
#Override
public void onChanged() {
synchronized(VerticalScrollView.this){
mDataChanged = true;
}
invalidate();
requestLayout();
}
#Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
EDIT: I can get this to work perfectly by using a plain ListView instead of my custom VerticalScrollView. I'm going to be populating the ListView from the network as the user scrolls down. How can I manage the list item views so that I don't run out of memory?
I was able to get the behavior that I wanted by attaching the HorizontalListViews to a ListView. I then wrapped my adapter that creates the HorizontalListViews with an EndlessAdapter for continuous scrolling:
https://github.com/commonsguy/cwac-endless
I made sure to recycle views in my adapter's getView method to address memory issues. Though, I can still run out of memory if my model layer takes up too much space on the heap.
Does anyone know of a good framework for culling model objects when they go off screen, and reloading them when they need to be displayed again?
I am using ViewPageIndicator for developing a pager view with its indicator
MyCode
Activity
public class Pager extends Activity{
private MyPagerAdapter mAdapter;
private ViewPager mPager;
private CirclePageIndicator mIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager);
mAdapter=new MyPagerAdapter(getApplicationContext());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (CirclePageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
MyPagerAdapter .java
public class MyPagerAdapter extends PagerAdapter{
protected static final String[] CONTENT = new String[] { "This", "Is", "A", "Test", "Demo","Sample","Example" ,
"Application","To","Study","And","Implement","Page-Viewer","And","Page-Indicator","For","News"};
private Context ctx;
private int mCount = CONTENT.length;
public MyPagerAdapter(Context ctx){
this.ctx = ctx;
}
#Override
public int getCount() {
return mCount;
}
#Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setGravity(Gravity.CENTER);
view.setTextSize(20 );
view.setPadding(20, 20, 20, 20);
view.setText(CONTENT[position]);
((ViewPager)collection).addView(view);
return view;
}
#Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
#Override
public void startUpdate(View arg0) {
}
#Override
public void finishUpdate(View arg0) {
}
}
I modified CirclePageIndicator.java created by JakeWharton to my use
public class CirclePageIndicator extends View implements PageIndicator {
private static final int INVALID_POINTER = -1;
private float mRadius;
private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG);
private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG);
private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG);
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mListener;
private int mCurrentPage;
private float mPageOffset;
private int mScrollState;
private int mOrientation;
private boolean mCentered;
private int mTouchSlop;
private float mLastMotionX = -1;
private int mActivePointerId = INVALID_POINTER;
private boolean mIsDragging;
int mCount = 0;
public CirclePageIndicator(Context context) {
this(context, null);
}
public CirclePageIndicator(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.vpiCirclePageIndicatorStyle);
}
public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (isInEditMode()) return;
//Load defaults from resources
final Resources res = getResources();
final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);
final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
//Retrieve styles attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);
mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation);
mPaintPageFill.setStyle(Style.FILL);
mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor));
mPaintStroke.setStyle(Style.STROKE);
mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
mPaintFill.setStyle(Style.FILL);
mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background);
if (background != null) {
setBackgroundDrawable(background);
}
a.recycle();
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
}
public void setCentered(boolean centered) {
mCentered = centered;
notifyDataSetChanged();
}
public boolean isCentered() {
return mCentered;
}
public void setFillColor(int fillColor) {
mPaintFill.setColor(fillColor);
notifyDataSetChanged();
}
public int getFillColor() {
return mPaintFill.getColor();
}
public void setOrientation(int orientation) {
switch (orientation) {
case HORIZONTAL:
case VERTICAL:
mOrientation = orientation;
requestLayout();
break;
default:
throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
}
}
public int getOrientation() {
return mOrientation;
}
public void setStrokeColor(int strokeColor) {
mPaintStroke.setColor(strokeColor);
notifyDataSetChanged();
}
public int getStrokeColor() {
return mPaintStroke.getColor();
}
public void setStrokeWidth(float strokeWidth) {
mPaintStroke.setStrokeWidth(strokeWidth);
notifyDataSetChanged();
}
public float getStrokeWidth() {
return mPaintStroke.getStrokeWidth();
}
public void setRadius(float radius) {
mRadius = radius;
notifyDataSetChanged();
}
public float getRadius() {
return mRadius;
}
boolean greaterThanFive = false;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mViewPager == null) {
return;
}
//if(!greaterThanFive){
mCount = mViewPager.getAdapter().getCount();
// }
int condition=5;
if(mCurrentPage>4)
condition=2;
if (mCount == 0) {
return;
}
if (mCurrentPage >= mCount) {
setCurrentItem(mCount-1);
return;
}
int longSize;
int longPaddingBefore;
int longPaddingAfter;
int shortPaddingBefore;
if (mOrientation == HORIZONTAL) {
longSize = getWidth();
longPaddingBefore = getPaddingLeft();
longPaddingAfter = getPaddingRight();
shortPaddingBefore = getPaddingTop();
} else {
longSize = getHeight();
longPaddingBefore = getPaddingTop();
longPaddingAfter = getPaddingBottom();
shortPaddingBefore = getPaddingLeft();
}
final float threeRadius = mRadius * 3;
final float shortOffset = shortPaddingBefore + mRadius;
float longOffset = longPaddingBefore + mRadius;
if (mCentered) {
longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((condition * threeRadius) / 2.0f);
}
float dX;
float dY;
float pageFillRadius = mRadius;
if (mPaintStroke.getStrokeWidth() > 0) {
pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f;
}
//Draw stroked circles
for (int iLoop = 0; iLoop < condition; iLoop++) {
float drawLong = longOffset + (iLoop * threeRadius);
if (mOrientation == HORIZONTAL) {
dX = drawLong;
dY = shortOffset;
} else {
dX = shortOffset;
dY = drawLong;
}
// Only paint fill if not completely transparent
if (mPaintPageFill.getAlpha() > 0) {
canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill);
}
// Only paint stroke if a stroke width was non-zero
if (pageFillRadius != mRadius) {
canvas.drawCircle(dX, dY, mRadius, mPaintStroke);
}
}
//Draw the filled circle according to the current scroll
float cx= mCurrentPage* threeRadius;
cx += mPageOffset * threeRadius;
if (mOrientation == HORIZONTAL) {
dX = longOffset + cx;
dY = shortOffset;
} else {
dX = shortOffset;
dY = longOffset + cx;
}
canvas.drawCircle(dX, dY, mRadius, mPaintFill);
}
public boolean onTouchEvent(android.view.MotionEvent ev) {
if (super.onTouchEvent(ev)) {
return true;
}
if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
return false;
}
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mLastMotionX = ev.getX();
break;
case MotionEvent.ACTION_MOVE: {
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final float deltaX = x - mLastMotionX;
if (!mIsDragging) {
if (Math.abs(deltaX) > mTouchSlop) {
mIsDragging = true;
}
}
if (mIsDragging) {
mLastMotionX = x;
if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
mViewPager.fakeDragBy(deltaX);
}
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (!mIsDragging) {
final int count = mViewPager.getAdapter().getCount();
final int width = getWidth();
final float halfWidth = width / 2f;
final float sixthWidth = width / 6f;
if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
if (action != MotionEvent.ACTION_CANCEL) {
mViewPager.setCurrentItem(mCurrentPage - 1);
}
return true;
} else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
if (action != MotionEvent.ACTION_CANCEL) {
mViewPager.setCurrentItem(mCurrentPage + 1);
}
return true;
}
}
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
mLastMotionX = MotionEventCompat.getX(ev, index);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
break;
}
return true;
}
#Override
public void setViewPager(ViewPager view) {
if (mViewPager == view) {
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
}
if (view.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.setOnPageChangeListener(this);
notifyDataSetChanged();
}
#Override
public void setViewPager(ViewPager view, int initialPosition) {
setViewPager(view);
setCurrentItem(initialPosition);
}
#Override
public void setCurrentItem(int item) {
if (mViewPager == null) {
throw new IllegalStateException("ViewPager has not been bound.");
}
mViewPager.setCurrentItem(item);
mCurrentPage = item;
notifyDataSetChanged();
}
#Override
public void notifyDataSetChanged() {
invalidate();
}
#Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;
if (mListener != null) {
mListener.onPageScrollStateChanged(state);
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mCurrentPage = position;
mPageOffset = positionOffset;
notifyDataSetChanged();
if (mListener != null) {
mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
#Override
public void onPageSelected(int position) {
/*if(position>4){
mCount=2;
greaterThanFive=true;
mCurrentPage = position;
notifyDataSetChanged();
return;
}*/
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
mCurrentPage = position;
notifyDataSetChanged();
}
if (mListener != null) {
mListener.onPageSelected(position);
}
}
#Override
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
mListener = listener;
}
public void onExcessPage(){
}
/*
* (non-Javadoc)
*
* #see android.view.View#onMeasure(int, int)
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == HORIZONTAL) {
setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
} else {
setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
}
}
/**
* Determines the width of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The width of the view, honoring constraints from measureSpec
*/
private int measureLong(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {
//We were told how big to be
result = specSize;
} else {
//Calculate the width according the views count
final int count = mViewPager.getAdapter().getCount();
result = (int)(getPaddingLeft() + getPaddingRight()
+ (count * 2 * mRadius) + (count - 1) * mRadius + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* Determines the height of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The height of the view, honoring constraints from measureSpec
*/
private int measureShort(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
//We were told how big to be
result = specSize;
} else {
//Measure the height
result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState)state;
super.onRestoreInstanceState(savedState.getSuperState());
mCurrentPage = savedState.currentPage;
requestLayout();
}
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.currentPage = mCurrentPage;
return savedState;
}
static class SavedState extends BaseSavedState {
int currentPage;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
currentPage = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(currentPage);
}
#SuppressWarnings("UnusedDeclaration")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
PageIndicator .java
public interface PageIndicator extends ViewPager.OnPageChangeListener {
void setViewPager(ViewPager view);
void setViewPager(ViewPager view, int initialPosition);
void setCurrentItem(int item);
void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
void notifyDataSetChanged();
}
My layout XML is
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<Button
android:id="#+id/btn_play"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:text="play" />
<Button
android:id="#+id/btn_pause"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:text="pause" />
<Button
android:id="#+id/btn_stop"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:text="stop" />
</LinearLayout>
<SeekBar
android:id="#+id/seekBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<com.example.demopager.CirclePageIndicator
android:id="#+id/indicator"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dip" />
</LinearLayout>
Here you can see there is more than 10 pages in my pager .The thing i want to do id when i reach the 6th page i want to redraw the page indicator to 2 dots with out selection.
For first 5 Page changes it should be like this
After when i move to next page(page no >5) i want to avoid the selection of indicator and show only two indicators
Hope you understand what's my need (My English is not that good)..
So Please suggest me how can achieve this
Courtesy :Patrik Ã…kerfeldt & Jake Wharton