I'm working on a HorizontalScrollview with Center lock. It is based off some code found in this stackoverflow thread:
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.