0

I'm working on a HorizontalScrollview with Center lock. It is based off some code found in this stackoverflow thread:

HorizontalScrollView with imageviews as a child and while scrolling the center of the image in the screen should play animation?

The issue is that it didn't support D-Pad navigation correctly. It wouldn't scroll to the next item but jump positions. I modified the code so that it worked with D-Pad naviagtion correctly, but that made the Touch scrolling not work. Also for some reason even though the TextViews are added correctly the click listeners seem to get removed. Anyways, the main issue now is that if I add back in the code for onScrollChange event, it works correctly for Touch, but the D-Pad navigation can skip an item if it was off screen and then brought back on screen.

The following is the xml.layout being used:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/serenity_bonsai_logo"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout
       android:id="@+id/main_menu_row1"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:layout_alignParentTop="true"
       android:orientation="horizontal" >

       <DigitalClock
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:background="#50000000"
           android:textColor="#F0F0F0"
           android:textSize="20sp" />
    </LinearLayout>

    <us.nineworlds.serenity.widgets.CenterLockHorizontalScrollview
        android:id="@+id/mainGalleryMenu"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#50000000"
        android:layout_centerInParent="true"
        android:clickable="true"
        android:scrollbars="none" >

        <LinearLayout
           android:layout_width="fill_parent"
           android:layout_height="150dp"
           android:layout_gravity="center_vertical"
           android:background="@android:color/transparent"
           android:orientation="horizontal"
           android:clickable="true" >
        </LinearLayout>
     </us.nineworlds.serenity.widgets.CenterLockHorizontalScrollview>

</RelativeLayout>

I know the issue is around the onScrollChange method and how it is calculating what should be the new center view, but not sure how to get D-Pad and touch scrolling playing nicely together. Here is the relevant code for the CenterLockHorizontalScrollView class.

public class CenterLockHorizontalScrollview extends HorizontalScrollView {
private ListAdapter mAdapter;

private int mCenterViewPosition = -1;
private MenuDataSetObserver menuDataObserver;
private OnItemSelectedListener selectedItemListener;
private boolean keypadScrollEvent = false;

public CenterLockHorizontalScrollview(Context context, AttributeSet attrs) {
    super(context, attrs);

    this.setHorizontalFadingEdgeEnabled(true);
    this.setHorizontalScrollBarEnabled(false);
    this.setFadingEdgeLength(5);
    this.setSmoothScrollingEnabled(true);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

    super.onLayout(changed, l, t, r, b);

    if (getChildCount() == 0) {
        return;
    }

    initCenterView();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (getChildCount() == 0)
        return;

    ViewGroup parent = (ViewGroup) getChildAt(0);

    if (parent.getChildCount() == 0)
        return;

    View FirstChild = parent.getChildAt(0);

    int LeftPadding = (getWidth() / 2)
            - (FirstChild.getMeasuredWidth() / 2);

    View LastChild = parent.getChildAt(getChildCount() - 1);

    int RightPadding = (getWidth() / 2)
            - (LastChild.getMeasuredWidth() / 2);

    if (parent.getPaddingLeft() != LeftPadding
            && parent.getPaddingRight() != RightPadding) {
        parent.setPadding(LeftPadding, parent.getPaddingTop(),
                RightPadding, parent.getPaddingBottom());
        requestLayout();
    }
}

/* (non-Javadoc)
 * @see android.view.View#onKeyDown(int, android.view.KeyEvent)
 */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    int totalItems = 0;
    if (mAdapter.getCount() > 0) {
        totalItems = mAdapter.getCount();
    }

    int currentPosition = mCenterViewPosition;

    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
        if (currentPosition > 0) {
            setSelectedIndex(currentPosition - 1);
        } else {
            setSelectedIndex(totalItems - 1);
        }
        keypadScrollEvent = true;
        scrollToSelectedIndex();
        keypadScrollEvent = false;
        return true;
    }

    if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
        if (currentPosition < totalItems - 1) {
            setSelectedIndex(currentPosition + 1);
        } else {
            setSelectedIndex(0);
        }
        keypadScrollEvent = true;
        scrollToSelectedIndex();
        keypadScrollEvent = false;
        return true;
    }

    if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
        keyCode == KeyEvent.KEYCODE_ENTER) {
        ViewGroup parent = (ViewGroup) getChildAt(0);
        View view = parent.getChildAt(mCenterViewPosition);
        view.setOnClickListener(new GalleryOnClickListener(view.getContext()));
        view.performClick();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}


private int getCenterPositionFromView() {

    int scrollingCenterView = getScrollingCenterView();
    if (mCenterViewPosition != scrollingCenterView) {
        ViewGroup parent = (ViewGroup) getChildAt(0);
        View view = parent.getChildAt(scrollingCenterView);
        selectedItemListener.onItemSelected(view, scrollingCenterView, scrollingCenterView);
        mCenterViewPosition = scrollingCenterView;
    }

    return scrollingCenterView;
}


private int getScrollingCenterView() {
     if(getChildCount() == 0)
            return -1;

        int centerView= 0;
        int centerX = getScrollX() + (getWidth() / 2); 

        ViewGroup parent = (ViewGroup) getChildAt(0);

        if(parent.getChildCount() == 0)
            return -1;

        View child = parent.getChildAt(0);

        while(child != null && child.getRight() <= centerX && centerView < parent.getChildCount())
        {
            centerView++;
            child = parent.getChildAt(centerView);
        }

        if(centerView >= parent.getChildCount()) {
            centerView = parent.getChildCount() - 1;
        }

        return centerView;      

}


public int getCenterViewPosition() {
    return mCenterViewPosition;
}

public ListAdapter getAdapter() {
    return mAdapter;
}

public void setAdapter(ListAdapter mAdapter) {

    this.mAdapter = mAdapter;
    if (menuDataObserver != null) {
        mAdapter.unregisterDataSetObserver(menuDataObserver);
    }
    mAdapter.registerDataSetObserver(new MenuDataSetObserver());
}

private void fillViewWithAdapter() {
    if (getChildCount() == 0 || mAdapter == null)
        return;

    ViewGroup parent = (ViewGroup) getChildAt(0);

    parent.removeAllViews();

    for (int i = 0; i < mAdapter.getCount(); i++) {
        parent.addView(mAdapter.getView(i, null, parent));
    }
    setSelectedIndex(0);
}



@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);

    if (keypadScrollEvent == false) {
        getCenterPositionFromView();
    }
    initCenterView();
}

private void initCenterView() {
    Log.i(getClass().getName(), "initCenterView");

    if (getChildCount() == 0)
        return;

    ViewGroup parent = (ViewGroup) getChildAt(0);

    if (parent.getChildCount() == 0)
        return;

    int centerView = getCenterViewPosition();

    if (centerView == -1) {
        mCenterViewPosition = 0;
        centerView = 0;
    }

    if (centerView != -1 && centerView != mCenterViewPosition
            && parent.getChildAt(0).getLeft() >= 0) {
        scrollToSelectedIndex();
    }

    if (centerView < 0 || centerView > parent.getChildCount())
        return;

    for (int i = 0; i <= parent.getChildCount(); i++) {

        if (! (parent.getChildAt(i) instanceof TextView)) {
            continue;
        }

        if (i == centerView) {
            // Start Animation
            //setSelectedIndex(i);
            //scrollToSelectedIndex();
            return;
        } else {
            // Remove Animation for other Views
        }
    }

}

public int getSelectedIndex() {
    return getCenterViewPosition();
}

public void setSelectedIndex(int index) {
    Log.i(getClass().getName(), "setSelectedIndex");

    if (getChildCount() == 0)
        return;

    ViewGroup parent = (ViewGroup) getChildAt(0);

    if (index < 0 || index > parent.getChildCount()) {
        throw new ArrayIndexOutOfBoundsException(index);
    }

    mCenterViewPosition = index;

    //onSelectedItemChanged.onSelectedChanged(this, mCenterViewPosition);
    selectedItemListener.onItemSelected(parent.getChildAt(index), index, 0); 

    requestLayout();
}

protected void scrollToSelectedIndex() {

    ViewGroup parent = (ViewGroup) getChildAt(0);

    View child = parent.getChildAt(mCenterViewPosition);

    if (child == null) {
        return;
    }

    int offsetX = ((child.getLeft() + child.getRight()) / 2) - (this.getWidth() / 2);

    smoothScrollTo(offsetX, 0);
}

public class MenuDataSetObserver extends DataSetObserver {

    @Override
    public void onChanged() {
        fillViewWithAdapter();
    }

}

public OnItemSelectedListener getOnItemSelectedListener() {
    return selectedItemListener;
}

public void setOnItemSelectedListener(OnItemSelectedListener selectedItemListener) {
    this.selectedItemListener = selectedItemListener;
}
}

With out the checks for center in onScrollChange D-Pad navigation works correctly but touch scrolling doesn't.

Community
  • 1
  • 1
kingargyle
  • 1,239
  • 10
  • 15

1 Answers1

0

My recommendation would be to not override the onKeyDown event and instead attach an onClickListener. Have you considered using a ViewPager instead where you override the ViewPager width to account for the items contained within?

Krispy
  • 1,098
  • 1
  • 7
  • 11
  • No haven't checked a ViewPager yet. Mainly I'm trying to move away from the Gallery view which is deprecated, and the modified horizontalscroll seemed like a good candidate. I had to override the onKeyDown event as otherwise the D-Pad keys were not responding as expected (i.e. skipping 2 to three items per press). – kingargyle Apr 22 '13 at 23:20
  • As long as your data set is small then Horizontal scroll will work nicely. If you have a large data set (or an infinite one) I would advise looking at ViewPager and overridding the width – Krispy Apr 22 '13 at 23:45