From b7af575417c119b94898f4b7f6b79fb8defc042d Mon Sep 17 00:00:00 2001 From: Aleksey Malevaniy Date: Thu, 14 Mar 2013 10:41:12 +0200 Subject: [PATCH 1/3] Git ignores intellij idea project files --- .gitignore | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d3f5d3e..d8626c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# svn +*.svn* + # built application files *.apk *.ap_ @@ -8,9 +11,29 @@ # Java class files *.class -# generated files -bin/ -gen/ +# generated GUI files +*/R.java + +# generated folder +bin +gen -# Local configuration file (sdk path, etc) +# local local.properties +proguard.cfg + +# log files +log*.txt + +# archives +*.gz +*.tar +*.zip + +# eclipse +*.metadata +*.settings + +#idea +*.idea +*.iml From 948dcfa21ffd6a18f54ebb3e219624e9bb83637a Mon Sep 17 00:00:00 2001 From: Aleksey Malevaniy Date: Thu, 14 Mar 2013 11:11:26 +0200 Subject: [PATCH 2/3] CHOICE_MODE support (SINGLE and MULTIPLE). But still no CHOICE_MODE_MULTIPLE_MODAL --- Demo/res/drawable/bg_item.xml | 7 + Demo/res/drawable/bg_item_activated.xml | 7 + Demo/res/drawable/bg_item_normal.xml | 7 + Demo/res/layout/{view1.xml => list_item.xml} | 9 +- .../CheckableLinearLayout.java | 62 +++ .../MainActivity.java | 28 +- .../widget/HorizontalVariableListView.java | 409 ++++++++++++++++-- 7 files changed, 480 insertions(+), 49 deletions(-) create mode 100644 Demo/res/drawable/bg_item.xml create mode 100644 Demo/res/drawable/bg_item_activated.xml create mode 100644 Demo/res/drawable/bg_item_normal.xml rename Demo/res/layout/{view1.xml => list_item.xml} (76%) create mode 100644 Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/CheckableLinearLayout.java diff --git a/Demo/res/drawable/bg_item.xml b/Demo/res/drawable/bg_item.xml new file mode 100644 index 0000000..297dc45 --- /dev/null +++ b/Demo/res/drawable/bg_item.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Demo/res/drawable/bg_item_activated.xml b/Demo/res/drawable/bg_item_activated.xml new file mode 100644 index 0000000..f2192be --- /dev/null +++ b/Demo/res/drawable/bg_item_activated.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/Demo/res/drawable/bg_item_normal.xml b/Demo/res/drawable/bg_item_normal.xml new file mode 100644 index 0000000..503d134 --- /dev/null +++ b/Demo/res/drawable/bg_item_normal.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/Demo/res/layout/view1.xml b/Demo/res/layout/list_item.xml similarity index 76% rename from Demo/res/layout/view1.xml rename to Demo/res/layout/list_item.xml index caa49eb..6088c88 100644 --- a/Demo/res/layout/view1.xml +++ b/Demo/res/layout/list_item.xml @@ -1,15 +1,15 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/CheckableLinearLayout.java b/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/CheckableLinearLayout.java new file mode 100644 index 0000000..ad5f230 --- /dev/null +++ b/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/CheckableLinearLayout.java @@ -0,0 +1,62 @@ +package it.sephiroth.android.sample.horizontalvariablelistviewdemo; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.Checkable; +import android.widget.LinearLayout; + +/** + * Copyright /(c/) 2013 Quotient Solutions. All rights reserved. + */ +public class CheckableLinearLayout extends LinearLayout implements Checkable { + + private boolean isChecked; + + public CheckableLinearLayout(Context context) { + super(context); + } + + public CheckableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleRes) { + super(context, attrs, defStyleRes); + } + + @Override + public void setChecked(boolean checked) { + isChecked = checked; + refreshDrawableState(); + } + + @Override + public boolean isChecked() { + return isChecked; + } + + @Override + public void toggle() { + setChecked(!isChecked); + } + + private static final int[] CHECKED_STATE_SET = { + android.R.attr.state_checked, + }; + + + @Override + protected int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) { + mergeDrawableStates(drawableState, CHECKED_STATE_SET); + } + return drawableState; + } + + @Override + public boolean performClick() { + toggle(); + return super.performClick(); + } +} diff --git a/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/MainActivity.java b/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/MainActivity.java index ccbc7e8..8403924 100644 --- a/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/MainActivity.java +++ b/Demo/src/it/sephiroth/android/sample/horizontalvariablelistviewdemo/MainActivity.java @@ -1,20 +1,16 @@ package it.sephiroth.android.sample.horizontalvariablelistviewdemo; -import it.sephiroth.android.library.widget.HorizontalVariableListView; -import it.sephiroth.android.library.widget.HorizontalVariableListView.OnLayoutChangeListener; -import java.util.ArrayList; -import java.util.List; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; +import android.view.*; +import android.widget.*; +import it.sephiroth.android.library.widget.HorizontalVariableListView; +import it.sephiroth.android.library.widget.HorizontalVariableListView.OnLayoutChangeListener; + +import java.util.ArrayList; +import java.util.List; public class MainActivity extends Activity { @@ -32,10 +28,18 @@ public void onCreate( Bundle savedInstanceState ) { data.add( String.valueOf( i ) ); } - ListAdapter adapter = new ListAdapter( this, R.layout.view1, R.layout.divider, data ); + ListAdapter adapter = new ListAdapter( this, R.layout.list_item, R.layout.divider, data ); mList.setOverScrollMode( View.OVER_SCROLL_ALWAYS ); mList.setEdgeGravityY( Gravity.BOTTOM ); mList.setAdapter( adapter ); + mList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + mList.setItemChecked(3, true); + mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Toast.makeText(MainActivity.this, "Clicked on # "+String.valueOf(position), Toast.LENGTH_SHORT).show(); + } + }); } diff --git a/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java b/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java index 6fefede..4c3046e 100644 --- a/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java +++ b/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java @@ -6,16 +6,6 @@ package it.sephiroth.android.library.widget; -import it.sephiroth.android.library.utils.DataSetObserverExtended; -import it.sephiroth.android.library.utils.ReflectionUtils; -import it.sephiroth.android.library.utils.ReflectionUtils.ReflectionException; -import it.sephiroth.android.library.widget.IFlingRunnable.FlingRunnableView; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; import android.content.Context; import android.database.DataSetObserver; import android.graphics.Canvas; @@ -25,21 +15,21 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; -import android.util.Log; -import android.view.GestureDetector; +import android.util.LongSparseArray; +import android.util.SparseBooleanArray; +import android.view.*; import android.view.GestureDetector.OnGestureListener; -import android.view.Gravity; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.SoundEffectConstants; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; import android.view.ViewTreeObserver.OnScrollChangedListener; import android.widget.AdapterView; +import android.widget.Checkable; import android.widget.ListAdapter; +import it.sephiroth.android.library.utils.DataSetObserverExtended; +import it.sephiroth.android.library.utils.ReflectionUtils; +import it.sephiroth.android.library.utils.ReflectionUtils.ReflectionException; +import it.sephiroth.android.library.widget.IFlingRunnable.FlingRunnableView; + +import java.lang.ref.WeakReference; +import java.util.*; // TODO: Auto-generated Javadoc /** @@ -506,19 +496,28 @@ protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) { /** * Adds the and measure child. - * - * @param child - * the child - * @param viewPos - * the view pos - */ - private void addAndMeasureChild( final View child, int viewPos ) { - LayoutParams params = child.getLayoutParams(); + * + * @param child + * the child + * @param viewPos + * @param position + */ + private void addAndMeasureChild(final View child, int viewPos, int position) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); if ( params == null ) { params = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ); } + if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { + if (child instanceof Checkable) { + ((Checkable) child).setChecked(mCheckStates.get(position)); + } else if (Math.min(getContext().getApplicationInfo().targetSdkVersion, Build.VERSION.SDK_INT) + >= Build.VERSION_CODES.HONEYCOMB) { + child.setActivated(mCheckStates.get(position)); + } + } + addViewInLayout( child, viewPos, params, false ); forceChildLayout( child, params ); } @@ -624,7 +623,7 @@ private void fillListLeft( int positionX, int leftEdge ) { while ( ( leftEdge - positionX ) > mLeftEdge && mLeftViewIndex >= 0 ) { int viewType = mAdapter.getItemViewType( mLeftViewIndex ); View child = mAdapter.getView( mLeftViewIndex, mRecycleBin.get( viewType ).poll(), this ); - addAndMeasureChild( child, 0 ); + addAndMeasureChild( child, 0, mLeftViewIndex); int childWidth = mChildWidths.get( viewType ); int childTop = getPaddingTop(); @@ -713,7 +712,7 @@ private void fillListRight( int positionX, int rightEdge ) { int viewType = mAdapter.getItemViewType( mRightViewIndex ); View child = mAdapter.getView( mRightViewIndex, mRecycleBin.get( viewType ).poll(), this ); - addAndMeasureChild( child, -1 ); + addAndMeasureChild( child, -1, mRightViewIndex); int childWidth = mChildWidths.get( viewType ); if ( childWidth == -1 ) { @@ -1536,7 +1535,8 @@ private boolean onItemClick( MotionEvent ev ) { if ( mOnItemClicked != null ) { playSoundEffect( SoundEffectConstants.CLICK ); mOnItemClicked.onItemClick( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); - } + performItemClick(child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } if ( mOnItemSelected != null ) { mOnItemSelected.onItemSelected( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); } @@ -1578,4 +1578,349 @@ public int getMaxX() { public void setDragTolerance( int value ) { mDragTolerance = value; } + + /** + * Normal list that does not indicate choices + */ + public static final int CHOICE_MODE_NONE = 0; + + /** + * The list allows up to one choice + */ + public static final int CHOICE_MODE_SINGLE = 1; + + /** + * The list allows multiple choices + */ + public static final int CHOICE_MODE_MULTIPLE = 2; + + /** + * The list allows multiple choices in a modal selection mode + */ + // public static final int CHOICE_MODE_MULTIPLE_MODAL = 3; + + /** + * Controls if/how the user may choose/check items in the list + */ + int mChoiceMode = CHOICE_MODE_NONE; + + /** + * Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive. + */ + ActionMode mChoiceActionMode; + + /** + * Running count of how many items are currently checked + */ + int mCheckedItemCount; + + /** + * Running state of which positions are currently checked + */ + SparseBooleanArray mCheckStates; + + /** + * Running state of which IDs are currently checked. + * If there is a value for a given key, the checked state for that ID is true + * and the value holds the last known position in the adapter for that id. + */ + LongSparseArray mCheckedIdStates; + + /** + * Returns the number of items currently selected. This will only be valid + * if the choice mode is not {@link #CHOICE_MODE_NONE} (default). + * + *

To determine the specific items that are currently selected, use one of + * the getChecked* methods. + * + * @return The number of items currently selected + * + * @see #getCheckedItemPosition() + * @see #getCheckedItemPositions() + * @see #getCheckedItemIds() + */ + public int getCheckedItemCount() { + return mCheckedItemCount; + } + + /** + * Returns the checked state of the specified position. The result is only + * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE} + * or {@link #CHOICE_MODE_MULTIPLE}. + * + * @param position The item whose checked state to return + * @return The item's checked state or false if choice mode + * is invalid + * + * @see #setChoiceMode(int) + */ + public boolean isItemChecked(int position) { + if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { + return mCheckStates.get(position); + } + + return false; + } + + /** + * Returns the currently checked item. The result is only valid if the choice + * mode has been set to {@link #CHOICE_MODE_SINGLE}. + * + * @return The position of the currently checked item or + * {@link #INVALID_POSITION} if nothing is selected + * + * @see #setChoiceMode(int) + */ + public int getCheckedItemPosition() { + if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) { + return mCheckStates.keyAt(0); + } + + return INVALID_POSITION; + } + + /** + * Returns the set of checked items in the list. The result is only valid if + * the choice mode has not been set to {@link #CHOICE_MODE_NONE}. + * + * @return A SparseBooleanArray which will return true for each call to + * get(int position) where position is a position in the list, + * or null if the choice mode is set to + * {@link #CHOICE_MODE_NONE}. + */ + public SparseBooleanArray getCheckedItemPositions() { + if (mChoiceMode != CHOICE_MODE_NONE) { + return mCheckStates; + } + return null; + } + + /** + * Returns the set of checked items ids. The result is only valid if the + * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter + * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true}) + * + * @return A new array which contains the id of each checked item in the + * list. + */ + public long[] getCheckedItemIds() { + if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) { + return new long[0]; + } + + final LongSparseArray idStates = mCheckedIdStates; + final int count = idStates.size(); + final long[] ids = new long[count]; + + for (int i = 0; i < count; i++) { + ids[i] = idStates.keyAt(i); + } + + return ids; + } + + /** + * Clear any choices previously set + */ + public void clearChoices() { + if (mCheckStates != null) { + mCheckStates.clear(); + } + if (mCheckedIdStates != null) { + mCheckedIdStates.clear(); + } + mCheckedItemCount = 0; + } + + /** + * Sets the checked state of the specified position. The is only valid if + * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or + * {@link #CHOICE_MODE_MULTIPLE}. + * + * @param position The item whose checked state is to be checked + * @param value The new checked state for the item + */ + public void setItemChecked(int position, boolean value) { + if (mChoiceMode == CHOICE_MODE_NONE) { + return; + } + + // Start selection mode if needed. We don't need to if we're unchecking something. + // if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) { + // mChoiceActionMode = startActionMode(mMultiChoiceModeCallback); + // } + + if (mChoiceMode == CHOICE_MODE_MULTIPLE /*|| mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL*/) { + boolean oldValue = mCheckStates.get(position); + mCheckStates.put(position, value); + if (mCheckedIdStates != null && mAdapter.hasStableIds()) { + if (value) { + mCheckedIdStates.put(mAdapter.getItemId(position), position); + } else { + mCheckedIdStates.delete(mAdapter.getItemId(position)); + } + } + if (oldValue != value) { + if (value) { + mCheckedItemCount++; + } else { + mCheckedItemCount--; + } + } + // if (mChoiceActionMode != null) { + // final long id = mAdapter.getItemId(position); + // mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, position, id, value); + // } + } else { + boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds(); + // Clear all values if we're checking something, or unchecking the currently + // selected item + if (value || isItemChecked(position)) { + mCheckStates.clear(); + if (updateIds) { + mCheckedIdStates.clear(); + } + } + // this may end up selecting the value we just cleared but this way + // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on + if (value) { + mCheckStates.put(position, true); + if (updateIds) { + mCheckedIdStates.put(mAdapter.getItemId(position), position); + } + mCheckedItemCount = 1; + } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { + mCheckedItemCount = 0; + } + } + + // Do not generate a data change while we are in the layout phase + // if (!mInLayout && !mBlockLayoutRequests) { + // mDataChanged = true; + // rememberSyncState(); + // } + requestLayout(); + } + + @Override + public boolean performItemClick(View view, int position, long id) { + boolean handled = false; + boolean dispatchItemClick = true; + + if (mChoiceMode != CHOICE_MODE_NONE) { + handled = true; + boolean checkedStateChanged = false; + + if (mChoiceMode == CHOICE_MODE_MULTIPLE /*|| + (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)*/) { + boolean newValue = !mCheckStates.get(position, false); + mCheckStates.put(position, newValue); + if (mCheckedIdStates != null && mAdapter.hasStableIds()) { + if (newValue) { + mCheckedIdStates.put(mAdapter.getItemId(position), position); + } else { + mCheckedIdStates.delete(mAdapter.getItemId(position)); + } + } + if (newValue) { + mCheckedItemCount++; + } else { + mCheckedItemCount--; + } + // if (mChoiceActionMode != null) { + // mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, + // position, id, newValue); + // dispatchItemClick = false; + // } + checkedStateChanged = true; + } else if (mChoiceMode == CHOICE_MODE_SINGLE) { + boolean newValue = !mCheckStates.get(position, false); + if (newValue) { + mCheckStates.clear(); + mCheckStates.put(position, true); + if (mCheckedIdStates != null && mAdapter.hasStableIds()) { + mCheckedIdStates.clear(); + mCheckedIdStates.put(mAdapter.getItemId(position), position); + } + mCheckedItemCount = 1; + } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { + mCheckedItemCount = 0; + } + checkedStateChanged = true; + } + + if (checkedStateChanged) { + updateOnScreenCheckedViews(); + } + } + + if (dispatchItemClick) { + handled |= super.performItemClick(view, position, id); + } + + return handled; + } + + /** + * Perform a quick, in-place update of the checked or activated state + * on all visible item views. This should only be called when a valid + * choice mode is active. + */ + private void updateOnScreenCheckedViews() { + final int firstPos = mLeftViewIndex + 1; + final int count = getChildCount(); + final boolean useActivated = Math.min(getContext().getApplicationInfo().targetSdkVersion,Build.VERSION.SDK_INT) + >= android.os.Build.VERSION_CODES.HONEYCOMB; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + final int position = firstPos + i; + + if (child instanceof Checkable) { + ((Checkable) child).setChecked(mCheckStates.get(position)); + } else if (useActivated) { + child.setActivated(mCheckStates.get(position)); + } + } + } + + /** + * @see #setChoiceMode(int) + * + * @return The current choice mode + */ + public int getChoiceMode() { + return mChoiceMode; + } + + /** + * Defines the choice behavior for the List. By default, Lists do not have any choice behavior + * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the + * List allows up to one item to be in a chosen state. By setting the choiceMode to + * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen. + * + * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or + * {@link #CHOICE_MODE_MULTIPLE} + */ + public void setChoiceMode(int choiceMode) { + mChoiceMode = choiceMode; + if (mChoiceActionMode != null) { + mChoiceActionMode.finish(); + mChoiceActionMode = null; + } + if (mChoiceMode != CHOICE_MODE_NONE) { + if (mCheckStates == null) { + mCheckStates = new SparseBooleanArray(); + } + if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { + mCheckedIdStates = new LongSparseArray(); + } + // Modal multi-choice mode only has choices when the mode is active. Clear them. + // if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { + // clearChoices(); + // setLongClickable(true); + // } + } + } + + } From f4100d1953a6a069437e5bb9afb8b3e0efb5091d Mon Sep 17 00:00:00 2001 From: Aleksey Malevaniy Date: Thu, 14 Mar 2013 12:27:09 +0200 Subject: [PATCH 3/3] Item click performed well and clickable property is needless. Long press is deprecated until implemented. --- Demo/res/layout/list_item.xml | 1 - .../widget/HorizontalVariableListView.java | 115 +++++++++--------- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/Demo/res/layout/list_item.xml b/Demo/res/layout/list_item.xml index 6088c88..4199df1 100644 --- a/Demo/res/layout/list_item.xml +++ b/Demo/res/layout/list_item.xml @@ -15,7 +15,6 @@ s diff --git a/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java b/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java index 4c3046e..b0dcb1f 100644 --- a/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java +++ b/HorizontalVariableListView/src/it/sephiroth/android/library/widget/HorizontalVariableListView.java @@ -320,8 +320,16 @@ public void setOnItemSelectedListener( AdapterView.OnItemSelectedListener listen public void setOnItemClickListener( AdapterView.OnItemClickListener listener ) { mOnItemClicked = listener; } + /** + * WARNING: not yet implemented, sorry. + */ + @Deprecated + @Override + public void setOnItemLongClickListener(OnItemLongClickListener listener) { + super.setOnItemLongClickListener(listener); + } - private DataSetObserverExtended mDataObserverExtended = new DataSetObserverExtended() { + private DataSetObserverExtended mDataObserverExtended = new DataSetObserverExtended() { @Override public void onAdded() { @@ -811,6 +819,36 @@ public boolean onFling( MotionEvent event0, MotionEvent event1, float velocityX, return true; } + private boolean onItemClick( MotionEvent ev ) { + if ( !mFlingRunnable.isFinished() || mWasFlinging ) return false; + + 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 ); + viewRect.offset( -mCurrentX, 0 ); + + if ( viewRect.contains( (int) ev.getX(), (int) ev.getY() ) ) { + if ( mOnItemClicked != null ) { + playSoundEffect( SoundEffectConstants.CLICK ); + mOnItemClicked.onItemClick( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); + performItemClick(child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } + if ( mOnItemSelected != null ) { + mOnItemSelected.onItemSelected( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); + performItemClick(child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); + } + break; + } + } + return true; + } + @Override public void onLongPress( MotionEvent e ) { if ( mWasFlinging ) return; @@ -986,7 +1024,12 @@ public boolean onInterceptTouchEvent( MotionEvent ev ) { if ( mIsDragging ) return false; final int action = ev.getAction(); - mGesture.onTouchEvent( ev ); + + /* + * We better get rid of gesture detector, it doesn't work properly for layouts/views without clickable property + * e.g. see AbsListView's mPendingCheckForLongPress usage in long press logic, must be implemented this way + */ + // mGesture.onTouchEvent( ev ); /* * Shortcut the most recurring case: the user is in the dragging state and he is moving his finger. We want to intercept this @@ -1226,7 +1269,11 @@ public boolean onTouchEvent( MotionEvent ev ) { if ( mFlingRunnable.isFinished() ) { scrollIntoSlots(); } - } + } else { + // assume it's a click + onItemClick(ev); + } + break; } @@ -1476,75 +1523,23 @@ protected void onFinishedMovement() { /** The m gesture listener. */ private OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onDoubleTap( MotionEvent e ) { - return false; - }; - - @Override - public boolean onSingleTapUp( MotionEvent e ) { - return onItemClick( e ); - }; - - @Override - public boolean onDown( MotionEvent e ) { - return false; - // return HorizontalFixedListView.this.onDown( e ); - }; - @Override public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) { return false; // return HorizontalFixedListView.this.onFling( e1, e2, velocityX, velocityY ); - }; - - @Override - public void onLongPress( MotionEvent e ) { - HorizontalVariableListView.this.onLongPress( e ); - }; + } @Override public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) { return false; // return HorizontalFixedListView.this.onScroll( e1, e2, distanceX, distanceY ); - }; - - @Override - public void onShowPress( MotionEvent e ) {}; - - @Override - public boolean onSingleTapConfirmed( MotionEvent e ) { - return true; } - private boolean onItemClick( MotionEvent ev ) { - if ( !mFlingRunnable.isFinished() || mWasFlinging ) return false; - - Rect viewRect = new Rect(); + @Override + public void onLongPress( MotionEvent e ) { + HorizontalVariableListView.this.onLongPress( e ); + } - 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 ); - viewRect.offset( -mCurrentX, 0 ); - - if ( viewRect.contains( (int) ev.getX(), (int) ev.getY() ) ) { - if ( mOnItemClicked != null ) { - playSoundEffect( SoundEffectConstants.CLICK ); - mOnItemClicked.onItemClick( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); - performItemClick(child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i)); - } - if ( mOnItemSelected != null ) { - mOnItemSelected.onItemSelected( HorizontalVariableListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ) ); - } - break; - } - } - return true; - } }; public View getChild( MotionEvent e ) {