OrizzontaleScrollView all'interno del trattamento con TouchLook ScrollView

Ho uno ScrollView che circonda l'integer layout in modo che l'integer schermo sia scrollabile. Il primo elemento che ho in questo ScrollView è un block HorizontalScrollView che dispone di funzionalità che possono essere scornetworking orizzontalmente. Ho aggiunto un ontouchlistener alla horizontalscrollview per gestire gli events a contatto e forzare la vista per "scattare" all'image più vicina dell'evento ACTION_UP.

Quindi l'effetto che vado per è come la home screen di android di riserva in cui è ansible scorrere tra di loro l'uno e l'altro e scatta in uno schermo quando si solleva il dito.

  • Come get la chiave dal valore in firebase
  • Android: Valuta EditText dopo che l'utente finisce di modificare
  • Android: ViewPager - PagerSlidingTabStrip, scheda di tabulazione personalizzata su state_selected
  • Invia notifica a tutti i dispositivi collegati a una networking Wi-Fi
  • Barra di progresso personalizzato Android
  • La vista della griglia non è illuminata quando si preme
  • Tutto questo funziona bene tranne un problema: devo spostare da sinistra a destra quasi perfettamente orizzontalmente per un ACTION_UP per registrarsi. Se faccio un minimo di verticale (che penso che molte persone tendono a fare sui loro telefoni quando si muovono lateralmente), riceverò un'azione ACTION_CANCEL anziché un'azione ACTION_UP. La mia teoria è che questo è dovuto al fatto che la visualizzazione horizontalscroll è all'interno di una scorrimento e la scorsa visualizzazione sta perrottare il touch verticale per consentire lo scorrimento verticale.

    Come posso distriggersre gli events touch per la visualizzazione di scorrimento da solo all'interno della mia scorciatoia orizzontale, ma permettere comunque la normale scorrimento verticale altrove nella vista di scorrimento?

    Ecco un esempio del mio codice:

    public class HomeFeatureLayout extends HorizontalScrollView { private ArrayList<ListItem> items = null; private GestureDetector gestureDetector; View.OnTouchListener gestureListener; private static final int SWIPE_MIN_DISTANCE = 5; private static final int SWIPE_THRESHOLD_VELOCITY = 300; private int activeFeature = 0; public HomeFeatureLayout(Context context, ArrayList<ListItem> items){ super(context); setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); setFadingEdgeLength(0); this.setHorizontalScrollBarEnabled(false); this.setVerticalScrollBarEnabled(false); LinearLayout internalWrapper = new LinearLayout(context); internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); internalWrapper.setOrientation(LinearLayout.HORIZONTAL); addView(internalWrapper); this.items = items; for(int i = 0; i< items.size();i++){ LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.homefeature,null); TextView header = (TextView) featureLayout.findViewById(R.id.featureheader); ImageView image = (ImageView) featureLayout.findViewById(R.id.featureimage); TextView title = (TextView) featureLayout.findViewById(R.id.featuretitle); title.setTag(items.get(i).GetLinkURL()); TextView date = (TextView) featureLayout.findViewById(R.id.featuredate); header.setText("FEATURED"); Image cachedImage = new Image(this.getContext(), items.get(i).GetImageURL()); image.setImageDrawable(cachedImage.getImage()); title.setText(items.get(i).GetTitle()); date.setText(items.get(i).GetDate()); internalWrapper.addView(featureLayout); } gestureDetector = new GestureDetector(new MyGestureDetector()); setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (gestureDetector.onTouchEvent(event)) { return true; } else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL ){ int scrollX = getScrollX(); int featureWidth = getMeasuredWidth(); activeFeature = ((scrollX + (featureWidth/2))/featureWidth); int scrollTo = activeFeature*featureWidth; smoothScrollTo(scrollTo, 0); return true; } else{ return false; } } }); } class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { try { //right to left if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { activeFeature = (activeFeature < (items.size() - 1))? activeFeature + 1:items.size() -1; smoothScrollTo(activeFeature*getMeasuredWidth(), 0); return true; } //left to right else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { activeFeature = (activeFeature > 0)? activeFeature - 1:0; smoothScrollTo(activeFeature*getMeasuredWidth(), 0); return true; } } catch (Exception e) { // nothing } return false; } } 

    }

  • Come utilizzare la camera Android in background?
  • Personalizza la visualizzazione della camera per Android utilizzando il browser
  • Sfondo Android TextView
  • Come creare il cerchio di forma di visualizzazione di text e impostare il colore di background diverso in base alla condizione
  • Android salva visualizzazione a jpg o png
  • Google GCM bloccò il mio server per submit richieste HTTPS
  • 8 Solutions collect form web for “OrizzontaleScrollView all'interno del trattamento con TouchLook ScrollView”

    Aggiornamento: ho capito questa cosa. Nel mio ScrollView, ho dovuto ignorare il metodo onInterceptTouchEvent per intercettare solo l'evento di contatto se il movimento Y è> il movimento X. Sembra che il comportmento predefinito di uno ScrollView è quello di intercettare l'evento di touch each volta che esiste un movimento ANY Y. Quindi, con la correzione, lo ScrollView intercetta solo l'evento se l'utente scorre in modo deliberatamente nella direzione Y e in quel caso passa l'ACTION_CANCEL ai bambini.

    Ecco il codice della mia class Scroll View che contiene l'orizzonte orizzontale:

     public class CustomScrollView extends ScrollView { private GestureDetector mGestureDetector; public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); mGestureDetector = new GestureDetector(context, new YScrollDetector()); setFadingEdgeLength(0); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev); } // Return false if we're scrolling in the x direction class YScrollDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return Math.abs(distanceY) > Math.abs(distanceX); } } } 

    Grazie Joel per averci dato un'idea su come risolvere questo problema.

    Ho semplificato il codice (senza bisogno di un GestureDetector ) per get lo stesso effetto:

     public class VerticalScrollView extends ScrollView { private float xDistance, yDistance, lastX, lastY; public VerticalScrollView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; lastX = ev.getX(); lastY = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - lastX); yDistance += Math.abs(curY - lastY); lastX = curX; lastY = curY; if(xDistance > yDistance) return false; } return super.onInterceptTouchEvent(ev); } } 

    Penso di aver trovato una soluzione più semplice, solo questa utilizza una sottoclass di ViewPager anziché (ScrollView).

    AGGIORNAMENTO 2013-07-16 : Ho aggiunto un override anche per onTouchEvent . Potrebbe forse aiutare con i problemi menzionati nei commenti, anche se YMMV.

     public class UninterceptableViewPager extends ViewPager { public UninterceptableViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean ret = super.onInterceptTouchEvent(ev); if (ret) getParent().requestDisallowInterceptTouchEvent(true); return ret; } @Override public boolean onTouchEvent(MotionEvent ev) { boolean ret = super.onTouchEvent(ev); if (ret) getParent().requestDisallowInterceptTouchEvent(true); return ret; } } 

    Questo è simile alla tecnica utilizzata in onScroll () di android.widget.Gallery . Viene spiegato ulteriormente la presentazione di Google I / O 2013 Scrivere le viste personalizzate per Android .

    Aggiornamento 2013-12-10 : Un approccio simile è descritto anche in un post di Kirill Grouchnikov riguardo l'app (appunto) di Android Market .

    Ho scoperto che qualche volta uno ScrollView riesce a concentrarsi e l'altro perde la messa a fuoco. È ansible impedire che, concedendo solo uno dei focus scrollView:

      scrollView1= (ScrollView) findViewById(R.id.scrollscroll); scrollView1.setAdapter(adapter); scrollView1.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { scrollView1.getParent().requestDisallowInterceptTouchEvent(true); return false; } }); 

    Grazie a Neevek la sua risposta ha funzionato per me, ma non blocca lo scorrimento verticale quando l'utente ha iniziato a scorrere la vista orizzontale (ViewPager) in direzione orizzontale e quindi senza sollevare il dito scorrere verticalmente inizia a scorrere la vista del contenitore sottostante (ScrollView) . Lo ho risolto facendo un leggero cambiamento nel codice di Neevak:

     private float xDistance, yDistance, lastX, lastY; int lastEvent=-1; boolean isLastEventIntercepted=false; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; lastX = ev.getX(); lastY = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - lastX); yDistance += Math.abs(curY - lastY); lastX = curX; lastY = curY; if(isLastEventIntercepted && lastEvent== MotionEvent.ACTION_MOVE){ return false; } if(xDistance > yDistance ) { isLastEventIntercepted=true; lastEvent = MotionEvent.ACTION_MOVE; return false; } } lastEvent=ev.getAction(); isLastEventIntercepted=false; return super.onInterceptTouchEvent(ev); } 

    Non funzionava bene per me. Lo ho cambiato e ora funziona senza problemi. Se qualcuno interessato.

     public class ScrollViewForNesting extends ScrollView{ private final int DIRECTION_VERTICAL = 0; private final int DIRECTION_HORIZONTAL = 1; private final int DIRECTION_NO_VALUE = -1; private final int mTouchSlop; private int mGestureDirection; private float mDistanceX; private float mDistanceY; private float mLastX; private float mLastY; public ScrollViewForNesting(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); } public ScrollViewForNesting(Context context, AttributeSet attrs) { this(context, attrs,0); } public ScrollViewForNesting(Context context) { this(context,null); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDistanceY = mDistanceX = 0f; mLastX = ev.getX(); mLastY = ev.getY(); mGestureDirection = DIRECTION_NO_VALUE; break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); mDistanceX += Math.abs(curX - mLastX); mDistanceY += Math.abs(curY - mLastY); mLastX = curX; mLastY = curY; break; } return super.onInterceptTouchEvent(ev) && shouldIntercept(); } private boolean shouldIntercept(){ if((mDistanceY > mTouchSlop || mDistanceX > mTouchSlop) && mGestureDirection == DIRECTION_NO_VALUE){ if(Math.abs(mDistanceY) > Math.abs(mDistanceX)){ mGestureDirection = DIRECTION_VERTICAL; } else{ mGestureDirection = DIRECTION_HORIZONTAL; } } if(mGestureDirection == DIRECTION_VERTICAL){ return true; } else{ return false; } } 

    }

    Questo divenne infine una parte della libreria di supporto v4, NestedScrollView . Quindi, non è più necessario eseguire hack locali per la maggior parte dei casi.

    La soluzione di Neevek funziona meglio di Joel sui dispositivi che eseguono 3,2 e sopra. C'è un bug in Android che causerà java.lang.IllegalArgumentException: pointerIndex fuori gamma se un rivelatore di gesti viene utilizzato all'interno di una scollview. Per duplicare il problema, implementa una scollview personalizzata come suggerito da Joel e inserito all'interno di un pager di visualizzazione. Se si trascina (non solleva figura) a una direzione (sinistra / destra) e poi all'altra, vedrai l'arresto. Anche nella soluzione di Joel, se si trascina il pager di visualizzazione spostando il dito in diagonale, una volta che il dito lascia l'area di visualizzazione del contenuto della pagina del pager, il pager ritorna alla sua posizione precedente. Tutti questi problemi hanno più a che fare con il design interno di Android o la mancanza di quella di Joel, che in sé è un pezzo di codice intelligente e conciso.

    http://code.google.com/p/android/issues/detail?id=18990

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.