Scorrimento Snappy in RecyclerView

Sto cercando di utilizzare la nuova class RecyclerView per uno scenario in cui voglio che il componente sfrutti un elemento specifico quando scorri (la vecchia Gallery Android viene a mente come esempio di un elenco con un elemento centrale bloccato).

Questo è l'approccio che sto portndo finora:

  • Come impostare AlarmManager ricorrente per eseguire il codice quotidianamente
  • Come hide la tastiera su schermo quando un DialogFragment viene annullato dall'evento setCanceledOnTouchOutside
  • Scelto file di file Android
  • Come get contatti telefonici in Android
  • Alcune domande su OAuth e Android
  • Ottenere la potenza del segnale WiFi della networking connessa
  • Ho un'interface, ISnappyLayoutManager , che contiene un metodo, getPositionForVelocity , che calcola in quale posizione la vista dovrebbe terminare lo scorrimento data la velocità di lancio iniziale.

     public interface ISnappyLayoutManager { int getPositionForVelocity(int velocityX, int velocityY); } 

    Poi ho una class, SnappyRecyclerView , che subclassica RecyclerView e sovrascrive il suo metodo fling () in modo da far scappare la vista l'importo esatto:

     public final class SnappyRecyclerView extends RecyclerView { /** other methods deleted **/ @Override public boolean fling(int velocityX, int velocityY) { LayoutManager lm = getLayoutManager(); if (lm instanceof ISnappyLayoutManager) { super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager()) .getPositionForVelocity(velocityX, velocityY)); } return true; } } 

    Non sono molto felice di questo approccio per diversi motivi. Innanzitutto, sembra in contrasto con la filosofia del 'RecyclerView' di subclassarla per implementare un certo tipo di scorrimento. In secondo luogo, se voglio utilizzare il LinearLayoutManager predefinito, questo diventa piuttosto complesso perché devo confondere con i suoi interni per capire il suo stato di scorrimento corrente e calcolare esattamente where questo scorre. Infine, questo non si occupa nemless di tutti i possibili scenari di scorrimento, come se si sposta l'elenco e quindi si ferma e poi solleva un dito, non si verifica un evento di fling (la velocità è troppo bassa) e quindi l'elenco rimane a metà strada posizione. Ciò può essere curato aggiungendo un listener di stato a scorrimento al RecyclerView , ma che si sente anche molto agguerrita.

    Mi sento come se manchi qualcosa. C'è un modo migliore per farlo?

    13 Solutions collect form web for “Scorrimento Snappy in RecyclerView”

    Ho finito per arrivare con qualcosa leggermente diverso da quello sopra. Non è ideale, ma funziona accettabilmente bene per me e può essere utile a qualcun altro. Non accetterò questa risposta nella speranza che qualcun altro sia accompagnato da qualcosa di meglio e less hacky (ed è ansible che io sono incomprensione dell'applicazione RecyclerView e manca un semplice modo di farlo, ma nel frattempo, questo è buono abbastanza per il lavoro del governo!)

    Le basi dell'attuazione sono queste: Lo scorrimento in un RecyclerView è una sorta di suddivisione tra il RecyclerView e il LinearLayoutManager . Ci sono due casi che devo gestire:

    1. L'utente fa scorrere la vista. Il comportmento predefinito è che il RecyclerView passa il lancio a uno Scroller interno che esegue quindi la magia di scorrimento. Ciò è problematico poiché allora il RecyclerView si solleva in una posizione instabile. Ho risolto questo problema, superando l'implementazione di RecyclerView fling() e, invece di lanciare, scorreggi la LinearLayoutManager in una posizione.
    2. L'utente solleva il dito con velocità insufficiente per avviare un rotolo. In questo caso non si verifica scappamento. Voglio rilevare questo caso nel caso in cui la vista non sia in posizione scattata. Lo faccio sostituendo il metodo onTouchEvent .

    Il SnappyRecyclerView :

     public final class SnappyRecyclerView extends RecyclerView { public SnappyRecyclerView(Context context) { super(context); } public SnappyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public SnappyRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean fling(int velocityX, int velocityY) { final LayoutManager lm = getLayoutManager(); if (lm instanceof ISnappyLayoutManager) { super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager()) .getPositionForVelocity(velocityX, velocityY)); return true; } return super.fling(velocityX, velocityY); } @Override public boolean onTouchEvent(MotionEvent e) { // We want the parent to handle all touch events--there's a lot going on there, // and there is no reason to overwrite that functionality--bad things will happen. final boolean ret = super.onTouchEvent(e); final LayoutManager lm = getLayoutManager(); if (lm instanceof ISnappyLayoutManager && (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) && getScrollState() == SCROLL_STATE_IDLE) { // The layout manager is a SnappyLayoutManager, which means that the // children should be snapped to a grid at the end of a drag or // fling. The motion event is either a user lifting their finger or // the cancellation of a motion events, so this is the time to take // over the scrolling to perform our own functionality. // Finally, the scroll state is idle--meaning that the resultant // velocity after the user's gesture was below the threshold, and // no fling was performsd, so the view may be in an unaligned state // and will not be flung to a proper state. smoothScrollToPosition(((ISnappyLayoutManager) lm).getFixScrollPos()); } return ret; } } 

    Un'interface per i gestori di layout snappy:

     /** * An interface that LayoutManagers that should snap to grid should implement. */ public interface ISnappyLayoutManager { /** * @param velocityX * @param velocityY * @return the resultant position from a fling of the given velocity. */ int getPositionForVelocity(int velocityX, int velocityY); /** * @return the position this list must scroll to to fix a state where the * views are not snapped to grid. */ int getFixScrollPos(); } 

    Ecco un esempio di un LayoutManager che subclass il LinearLayoutManager per get un LayoutManager con uno scorrimento regolare:

     public class SnappyLinearLayoutManager extends LinearLayoutManager implements ISnappyLayoutManager { // These variables are from android.widget.Scroller, which is used, via ScrollerCompat, by // Recycler View. The scrolling distance calculation logic originates from the same place. Want // to use their variables so as to approximate the look of normal Android scrolling. // Find the Scroller fling implementation in android.widget.Scroller.fling(). private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); private static double FRICTION = 0.84; private double deceleration; public SnappyLinearLayoutManager(Context context) { super(context); calculateDeceleration(context); } public SnappyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); calculateDeceleration(context); } private void calculateDeceleration(Context context) { deceleration = SensorManager.GRAVITY_EARTH // g (m/s^2) * 39.3700787 // inches per meter // pixels per inch. 160 is the "default" dpi, ie one dip is one pixel on a 160 dpi // screen * context.getResources().getDisplayMetrics().density * 160.0f * FRICTION; } @Override public int getPositionForVelocity(int velocityX, int velocityY) { if (getChildCount() == 0) { return 0; } if (getOrientation() == HORIZONTAL) { return calcPosForVelocity(velocityX, getChildAt(0).getLeft(), getChildAt(0).getWidth(), getPosition(getChildAt(0))); } else { return calcPosForVelocity(velocityY, getChildAt(0).getTop(), getChildAt(0).getHeight(), getPosition(getChildAt(0))); } } private int calcPosForVelocity(int velocity, int scrollPos, int childSize, int currPos) { final double dist = getSplineFlingDistance(velocity); final double tempScroll = scrollPos + (velocity > 0 ? dist : -dist); if (velocity < 0) { // Not sure if I need to lower bound this here. return (int) Math.max(currPos + tempScroll / childSize, 0); } else { return (int) (currPos + (tempScroll / childSize) + 1); } } @Override public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) { final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { // I want a behavior where the scrolling always snaps to the beginning of // the list. Snapping to end is also trivial given the default implementation. // If you need a different behavior, you may need to override more // of the LinearSmoothScrolling methods. protected int getHorizontalSnapPreference() { return SNAP_TO_START; } protected int getVerticalSnapPreference() { return SNAP_TO_START; } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return SnappyLinearLayoutManager.this .computeScrollVectorForPosition(targetPosition); } }; linearSmoothScroller.setTargetPosition(position); startSmoothScroll(linearSmoothScroller); } private double getSplineFlingDistance(double velocity) { final double l = getSplineDeceleration(velocity); final double decelMinusOne = DECELERATION_RATE - 1.0; return ViewConfiguration.getScrollFriction() * deceleration * Math.exp(DECELERATION_RATE / decelMinusOne * l); } private double getSplineDeceleration(double velocity) { return Math.log(INFLEXION * Math.abs(velocity) / (ViewConfiguration.getScrollFriction() * deceleration)); } /** * This implementation obviously doesn't take into account the direction of the * that preceded it, but there is no easy way to get that information without more * hacking than I was willing to put into it. */ @Override public int getFixScrollPos() { if (this.getChildCount() == 0) { return 0; } final View child = getChildAt(0); final int childPos = getPosition(child); if (getOrientation() == HORIZONTAL && Math.abs(child.getLeft()) > child.getMeasuredWidth() / 2) { // Scrolled first view more than halfway offscreen return childPos + 1; } else if (getOrientation() == VERTICAL && Math.abs(child.getTop()) > child.getMeasuredWidth() / 2) { // Scrolled first view more than halfway offscreen return childPos + 1; } return childPos; } } 

    Con LinearSnapHelper , questo è ora molto facile.

    Tutto quello che devi fare è questo:

     SnapHelper helper = new LinearSnapHelper(); helper.attachToRecyclerView(recyclerView); 

    È così semplice! Si noti che LinearSnapHelper stato aggiunto nella libreria di supporto a partire dalla versione 24.2.0.

    Significato è necessario aggiungere questo al build.gradle del module app

     compile "com.android.support:recyclerview-v7:24.2.0" 

    Sono riuscito a trovare un modo più pulito per farlo. @Catherine (OP) mi fa sapere se questo può essere migliorato o ti senti che è un miglioramento rispetto al tuo 🙂

    Ecco l'ascoltatore di scorrimento che uso.

    https://github.com/humblerookie/centerlockrecyclerview/

    Ho omesso alcune ipotesi minime qui come ad esempio.

    1) Paddings iniziali e finali : le prime e le ultime voci nel rotolo orizzontale devono avere le paddings iniziali e finali rispettivamente impostate in modo che le viste iniziali e finali siano al centro quando scornetworking rispettivamente per primo e ultimo. Per esempio, in onBindViewHolder si potrebbe fare qualcosa come questo.

     @Override public void onBindViewHolder(ReviewHolder holder, int position) { holder.container.setPadding(0,0,0,0);//Resetpadding if(position==0){ //Only one element if(mData.size()==1){ holder.container.setPadding(totalpaddinginit/2,0,totalpaddinginit/2,0); } else{ //>1 elements assign only initpadding holder.container.setPadding(totalpaddinginit,0,0,0); } } else if(position==mData.size()-1){ holder.container.setPadding(0,0,totalpaddingfinal,0); } } public class ReviewHolder extends RecyclerView.ViewHolder { protected TextView tvName; View container; public ReviewHolder(View itemView) { super(itemView); container=itemView; tvName= (TextView) itemView.findViewById(R.id.text); } } 

    La logica è prettty generic e si può usare per molti altri casi. Il mio caso è che la vista del riciclatore è orizzontale e allunga l'intera width orizzontale senza margini (in sostanza il centro della riciclata è il centro della schermata X) o le compensazioni irregolari.

    Incase chiunque si trova ad affrontare il commento gentile commento.

    Ho anche bisogno di una vista snappy recycler. Voglio lasciare che l'articolo di visualizzazione del riciclatore schiocco a sinistra di una colonna. Si è conclusa con l'implementazione di un SnapScrollListener che ho impostato sulla visualizzazione del riciclatore. Questo è il mio codice:

    SnapScrollListener:

     class SnapScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (RecyclerView.SCROLL_STATE_IDLE == newState) { final int scrollDistance = getScrollDistanceOfColumnClosestToLeft(mRecyclerView); if (scrollDistance != 0) { mRecyclerView.smoothScrollBy(scrollDistance, 0); } } } } 

    Calcolo dello snap:

     private int getScrollDistanceOfColumnClosestToLeft(final RecyclerView recyclerView) { final LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); final RecyclerView.ViewHolder firstVisibleColumnViewHolder = recyclerView.findViewHolderForAdapterPosition(manager.findFirstVisibleItemPosition()); if (firstVisibleColumnViewHolder == null) { return 0; } final int columnWidth = firstVisibleColumnViewHolder.itemView.getMeasuredWidth(); final int left = firstVisibleColumnViewHolder.itemView.getLeft(); final int absoluteLeft = Math.abs(left); return absoluteLeft <= (columnWidth / 2) ? left : columnWidth - absoluteLeft; } 

    Se la prima vista visibile viene scorsa più della metà di width fuori dalla schermata, la colonna visibile successiva sta scattando a sinistra.

    Impostazione dell'ascoltatore:

     mRecyclerView.addOnScrollListener(new SnapScrollListener()); 

    Ecco un hack più semplice per un semplice scorrimento a una certa posizione in un evento che scompare:

     @Override public boolean fling(int velocityX, int velocityY) { smoothScrollToPosition(position); return super.fling(0, 0); } 

    Sovrascrivere il metodo di fling con una chiamata a smoothScrollToPosition (posizione int), where "posizione int" è la posizione della vista che si desidera nella scheda. Sarà necessario get il valore della posizione in qualche modo, ma dipende dalle tue esigenze e dall'attuazione.

    Un approccio molto semplice per il raggiungimento di un comportmento a scatto –

      recyclerView.setOnScrollListener(new OnScrollListener() { private boolean scrollingUp; @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // Or use dx for horizontal scrolling scrollingUp = dy < 0; } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { // Make sure scrolling has stopped before snapping if (newState == RecyclerView.SCROLL_STATE_IDLE) { // layoutManager is the recyclerview's layout manager which you need to have reference in advance int visiblePosition = scrollingUp ? layoutManager.findFirstVisibleItemPosition() : layoutManager.findLastVisibleItemPosition(); int completelyVisiblePosition = scrollingUp ? layoutManager .findFirstCompletelyVisibleItemPosition() : layoutManager .findLastCompletelyVisibleItemPosition(); // Check if we need to snap if (visiblePosition != completelyVisiblePosition) { recyclerView.smoothScrollToPosition(visiblePosition); return; } } }); 

    L'unico inconveniente è che non scatta indietro quando si scorge less della metà della cella parzialmente visibile – ma se questo non ti preoccupa di una soluzione pulita e semplice.

    Dopo aver messo in giro con RecyclerView per un po ', questo è quello che ho scoperto finora e quello che sto usando adesso. Ha un difetto minore, ma non spargerò i fagioli (ancora) poiché probabilmente non ti accorgerai.

    https://gist.github.com/lauw/fc84f7d04f8c54e56d56

    Support solo i recyclerviews orizzontali e si avvicina al centro e può anche ridurre le viste in base a quanto sono lontane dal centro. Utilizzo come sostituzione di RecyclerView.

    Modifica: 08/2016 Realizzato in un repository:
    https://github.com/lauw/Android-SnappingRecyclerView
    Continuerò a continuare a lavorare su una migliore implementazione.

    Ho implementato una soluzione di lavoro per l'orientamento orizzontale di RecyclerView, che legge le coordinate suTouchEvent, sul primo MOVE e su UP. Su UP calcolare la posizione in cui dobbiamo andare.

     public final class SnappyRecyclerView extends RecyclerView { private Point mStartMovePoint = new Point( 0, 0 ); private int mStartMovePositionFirst = 0; private int mStartMovePositionSecond = 0; public SnappyRecyclerView( Context context ) { super( context ); } public SnappyRecyclerView( Context context, AttributeSet attrs ) { super( context, attrs ); } public SnappyRecyclerView( Context context, AttributeSet attrs, int defStyle ) { super( context, attrs, defStyle ); } @Override public boolean onTouchEvent( MotionEvent e ) { final boolean ret = super.onTouchEvent( e ); final LayoutManager lm = getLayoutManager(); View childView = lm.getChildAt( 0 ); View childViewSecond = lm.getChildAt( 1 ); if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_MOVE && mStartMovePoint.x == 0) { mStartMovePoint.x = (int)e.getX(); mStartMovePoint.y = (int)e.getY(); mStartMovePositionFirst = lm.getPosition( childView ); if( childViewSecond != null ) mStartMovePositionSecond = lm.getPosition( childViewSecond ); }// if MotionEvent.ACTION_MOVE if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_UP ){ int currentX = (int)e.getX(); int width = childView.getWidth(); int xMovement = currentX - mStartMovePoint.x; // move back will be positive value final boolean moveBack = xMovement > 0; int calculatedPosition = mStartMovePositionFirst; if( moveBack && mStartMovePositionSecond > 0 ) calculatedPosition = mStartMovePositionSecond; if( Math.abs( xMovement ) > ( width / 3 ) ) calculatedPosition += moveBack ? -1 : 1; if( calculatedPosition >= getAdapter().getItemCount() ) calculatedPosition = getAdapter().getItemCount() -1; if( calculatedPosition < 0 || getAdapter().getItemCount() == 0 ) calculatedPosition = 0; mStartMovePoint.x = 0; mStartMovePoint.y = 0; mStartMovePositionFirst = 0; mStartMovePositionSecond = 0; smoothScrollToPosition( calculatedPosition ); }// if MotionEvent.ACTION_UP return ret; }} 

    Funziona bene per me, fammi sapere se c'è qualcosa che non va.

    Per aggiornare la risposta di humblerookie:

    Questo listener di scorrimento è effettivamente efficace per il block del centro https://github.com/humblerookie/centerlockrecyclerview/

    Ma qui è un modo più semplice per aggiungere l'imbottitura all'inizio e alla fine del recyclerview per centrare i suoi elementi:

     mRecycler.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int childWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CHILD_WIDTH_IN_DP, getResources().getDisplayMetrics()); int offset = (mRecycler.getWidth() - childWidth) / 2; mRecycler.setPadding(offset, mRecycler.getPaddingTop(), offset, mRecycler.getPaddingBottom()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mRecycler.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { mRecycler.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } }); 

    Se hai bisogno di un supporto per l'inizio, la parte superiore, la fine o la parte inferiore, utilizza GravitySnapHelper ( https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapHelper .java ).

    Centro di sospensione:

     SnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(recyclerView); 

    Inizio di scatto con GravitySnapHelper:

     startRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); SnapHelper snapHelperStart = new GravitySnapHelper(Gravity.START); snapHelperStart.attachToRecyclerView(startRecyclerView); 

    Piano di scatto con GravitySnapHelper:

     topRecyclerView.setLayoutManager(new LinearLayoutManager(this)); SnapHelper snapHelperTop = new GravitySnapHelper(Gravity.TOP); snapHelperTop.attachToRecyclerView(topRecyclerView); 

    E ancora un'altra opzione pulita è quella di utilizzare LayoutManager personalizzato, è ansible controllare https://github.com/apptik/multiview/tree/master/layoutmanagers

    È in fase di sviluppo, ma funziona abbastanza bene. È disponibile un'istantanea: https://oss.sonatype.org/content/repositories/snapshots/io/apptik/multiview/layoutmanagers/

    Esempio:

     recyclerView.setLayoutManager(new SnapperLinearLayoutManager(getActivity())); 

    Avevo bisogno di qualcosa di diverso da tutte le risposte di cui sopra.

    I principali requisiti erano:

    1. Funziona altrettanto quando l'utente scoraggia o rilascia il dito.
    2. Utilizza il meccanismo di scorrimento natale per avere la stessa "sensazione" di un regolare RecyclerView .
    3. Quando si ferma, si avvia lo scorrimento liscio al punto di schiocco più vicino.
    4. Non è necessario utilizzare LayoutManager o RecyclerView . Solo un RecyclerView.OnScrollListener , che viene quindi allegato da recyclerView.addOnScrollListener(snapScrollListener) . In questo modo il codice è molto più pulito.

    E due requisiti molto specifici che dovrebbero essere facili da modificare nell'esempio qui sotto per adattarsi al tuo caso:

    1. Funziona orizzontalmente.
    2. Aggancia il bordo sinistro dell'elemento a un punto specifico del RecyclerView .

    Questa soluzione utilizza il nativo LinearSmoothScroller . La differenza è che nel passaggio finale, quando viene trovata la "visualizzazione di destinazione", modifica il calcolo dell'offset in modo che si scatta in una posizione specifica.

     public class SnapScrollListener extends RecyclerView.OnScrollListener { private static final float MILLIS_PER_PIXEL = 200f; /** The x coordinate of recycler view to which the items should be scrolled */ private final int snapX; int prevState = RecyclerView.SCROLL_STATE_IDLE; int currentState = RecyclerView.SCROLL_STATE_IDLE; public SnapScrollListener(int snapX) { this.snapX = snapX; } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); currentState = newState; if(prevState != RecyclerView.SCROLL_STATE_IDLE && currentState == RecyclerView.SCROLL_STATE_IDLE ){ performSnap(recyclerView); } prevState = currentState; } private void performSnap(RecyclerView recyclerView) { for( int i = 0 ;i < recyclerView.getChildCount() ; i ++ ){ View child = recyclerView.getChildAt(i); final int left = child.getLeft(); int right = child.getRight(); int halfWidth = (right - left) / 2; if (left == snapX) return; if (left - halfWidth <= snapX && left + halfWidth >= snapX) { //check if child is over the snapX position int adapterPosition = recyclerView.getChildAdapterPosition(child); int dx = snapX - left; smoothScrollToPositionWithOffset(recyclerView, adapterPosition, dx); return; } } } private void smoothScrollToPositionWithOffset(RecyclerView recyclerView, int adapterPosition, final int dx) { final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if( layoutManager instanceof LinearLayoutManager) { LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { @Override public PointF computeScrollVectorForPosition(int targetPosition) { return ((LinearLayoutManager) layoutManager).computeScrollVectorForPosition(targetPosition); } @Override protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference()); final int distance = (int) Math.sqrt(dx * dx + dy * dy); final int time = calculateTimeForDeceleration(distance); if (time > 0) { action.update(-dx, -dy, time, mDecelerateInterpolator); } } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return MILLIS_PER_PIXEL / displayMetrics.densityDpi; } }; scroller.setTargetPosition(adapterPosition); layoutManager.startSmoothScroll(scroller); } } 
    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.