Note that there are some explanatory texts on larger screens.

plurals
  1. POClick items inside HorizontalListView in Android
    text
    copied!<p>I have been using the HorizontalListView sample provided at <a href="http://www.dev-smart.com/archives/34" rel="nofollow noreferrer">http://www.dev-smart.com/archives/34</a>. However my list item has multiple items, which include an image and a button. When I click on the list item, I am able to trigger the onItemClicked &amp; onItemSelected listener, however I want to propagate the touch event down to the button from the list view, is that possible?</p> <p>My XML file is </p> <pre><code>&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item" android:orientation="vertical" android:layout_width="160dip" android:layout_height="160dip" android:padding="5dip" &gt; &lt;ImageView android:id="@+id/image" android:layout_width="100dip" android:layout_height="100dip" android:scaleType="centerCrop" android:src="@drawable/icon" /&gt; &lt;Button android:id="@+id/click" android:text="Add" android:layout_width="55dip" android:layout_height="40dip" android:layout_alignRight="@id/image" android:layout_alignBottom="@id/image" android:layout_above="@id/image" android:textSize="14dip" android:clickable="true" android:textColor="#FFFFFF" android:focusable="false" /&gt; </code></pre> <p></p> <p>My HorizontalListView.java is as follows</p> <pre><code>import java.util.LinkedList; import java.util.Queue; import android.content.Context; import android.database.DataSetObserver; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.ListAdapter; import android.widget.Scroller; public class HorizontalListView extends AdapterView&lt;ListAdapter&gt; { 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&lt;View&gt; mRemovedViewQueue = new LinkedList&lt;View&gt;(); private OnItemSelectedListener mOnItemSelected; private OnItemClickListener mOnItemClicked; private boolean mDataChanged = false; protected Button touchedButton = null; public HorizontalListView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public HorizontalListView(Context context) { super(context); 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; } private DataSetObserver mDataObserver = new DataSetObserver() { @Override public void onChanged() { synchronized(HorizontalListView.this){ mDataChanged = true; } 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 &lt; 0){ mNextX = 0; mScroller.forceFinished(true); } if(mNextX &gt; 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 &lt; getWidth() &amp;&amp; mRightViewIndex &lt; 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(); } mRightViewIndex++; } } private void fillListLeft(int leftEdge, final int dx) { while(leftEdge + dx &gt; 0 &amp;&amp; mLeftViewIndex &gt;= 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 &amp;&amp; child.getRight() + dx &lt;= 0) { mDisplayOffset += child.getMeasuredWidth(); mRemovedViewQueue.offer(child); removeViewInLayout(child); mLeftViewIndex++; child = getChildAt(0); } child = getChildAt(getChildCount()-1); while(child != null &amp;&amp; child.getLeft() + dx &gt;= getWidth()) { mRemovedViewQueue.offer(child); removeViewInLayout(child); mRightViewIndex--; child = getChildAt(getChildCount()-1); } } private void positionItems(final int dx) { if(getChildCount() &gt; 0){ mDisplayOffset += dx; int left = mDisplayOffset; for(int i=0;i&lt;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) { synchronized(HorizontalListView.this){ mNextX += (int)distanceX; } requestLayout(); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { Rect viewRect = new Rect(); for(int i=0;i&lt;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; } }; } </code></pre> <p>I have also tried suggestions given here <a href="https://stackoverflow.com/questions/895341/custom-list-clicking-with-checkboxes-in-android">Custom list clicking with checkboxes</a> but no luck so far. Really appreciate if someone could provide some insights into the way touch event is handled by such a custom view with horizontal scrolling.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload