Cómo supercrollar una vista personalizada (vista de superficie)

Tengo un SurfaceView de encargo que es manejado por un hilo del trabajador. Estoy usando el código muy similar al código en la entrada siguiente del blog para manejar el SurfaceView:

Http://android-coding.blogspot.com/2011/05/drawing-on-surfaceview.html

Mi SurfaceView personalizado se puede desplazar en el sentido de que escucho los eventos de toque, los envío a un detector de gestos e implemento onScroll. Hago un seguimiento de las distancias de desplazamiento en algunas variables miembro (tanto el eje x como el eje y) y traduce todas las coordenadas por las cantidades apropiadas al dibujar al lienzo. También sujeto las distancias de desplazamiento y puedo calcular y almacenar fácilmente cualquier cantidad de overscroll cuando pinza.

Todo esto funciona bien.

El problema es que quiero mostrar los efectos estándar de overscroll de Android en mi SurfaceView personalizado. Intenté llamar a overScrollBy manualmente pero no funcionó y mi mejor conjetura es porque estoy dibujando la visión de un hilo del trabajador que signifique que el onDraw de la opinión nunca se llama.

Encontré la siguiente publicación stackoverflow sobre cómo personalizar los efectos de sobrecroll:

¿Cómo puedo cambiar el color de OverScroll en Android 2.3.1?

Ese post no está destinado a SurfaceViews, pero probablemente podría adaptar el código. Dicho esto, ¿hay una mejor manera? Quiero mostrar exactamente los mismos efectos de overscroll que se muestran en otra parte. Hacer copias de los elementos desplegables y tratar de duplicar la lógica de overscroll parece … feo.

Bueno, me las arreglé para armar un ejemplo simple de overscroll en simple View usando OverScroller:

package net.project.experimental; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.widget.OverScroller; public class WorksheetView extends View { protected static final int OVERSCROLL_DISTANCE = 10; protected static final int INVALID_POINTER_ID = -1; private int fWorksheetWidth = 2000; private int fWorksheetHeight = 2000; private OverScroller fScroller; private VelocityTracker fVelocityTracker = null; private int fMinimumVelocity; // The 'active pointer' is the one currently moving our object. private int fTranslatePointerId = INVALID_POINTER_ID; private PointF fTranslateLastTouch = new PointF( ); private boolean fInteracting = false; public WorksheetView(Context context, AttributeSet attrs) { super( context, attrs ); this.initView( context, attrs ); } public WorksheetView(Context context, AttributeSet attrs, int defStyle) { super( context, attrs, defStyle ); this.initView( context, attrs ); } protected void initView(Context context, AttributeSet attrs) { fScroller = new OverScroller( this.getContext( ) ); this.setOverScrollMode( OVER_SCROLL_ALWAYS ); final ViewConfiguration configuration = ViewConfiguration.get( getContext( ) ); //fTouchSlop = configuration.getScaledTouchSlop( ); fMinimumVelocity = configuration.getScaledMinimumFlingVelocity( ); } @Override public boolean onTouchEvent(MotionEvent event) { if ( fVelocityTracker == null ) { fVelocityTracker = VelocityTracker.obtain( ); } fVelocityTracker.addMovement( event ); final int action = event.getAction( ); switch ( action & MotionEvent.ACTION_MASK ) { case MotionEvent.ACTION_DOWN: { if ( !fScroller.isFinished( ) ) fScroller.abortAnimation( ); final float x = event.getX( ); final float y = event.getY( ); fTranslateLastTouch.set( x, y ); fTranslatePointerId = event.getPointerId( 0 ); this.startInteracting( ); break; } case MotionEvent.ACTION_MOVE: { final int pointerIndexTranslate = event.findPointerIndex( fTranslatePointerId ); if ( pointerIndexTranslate >= 0 ) { float translateX = event.getX( pointerIndexTranslate ); float translateY = event.getY( pointerIndexTranslate ); this.overScrollBy( (int) (fTranslateLastTouch.x - translateX), (int) (fTranslateLastTouch.y - translateY), this.getScrollX( ), this.getScrollY( ), fWorksheetWidth - this.getWidth( ), fWorksheetHeight - this.getHeight( ), OVERSCROLL_DISTANCE, OVERSCROLL_DISTANCE, true ); fTranslateLastTouch.set( translateX, translateY ); this.invalidate( ); } break; } case MotionEvent.ACTION_UP: { final VelocityTracker velocityTracker = fVelocityTracker; velocityTracker.computeCurrentVelocity( 1000 ); //velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialXVelocity = (int) velocityTracker.getXVelocity( ); int initialYVelocity = (int) velocityTracker.getYVelocity( ); if ( (Math.abs( initialXVelocity ) + Math.abs( initialYVelocity ) > fMinimumVelocity) ) { this.fling( -initialXVelocity, -initialYVelocity ); } else { if ( fScroller.springBack( this.getScrollX( ), this.getScrollY( ), 0, fWorksheetWidth - this.getWidth( ), 0, fWorksheetHeight - this.getHeight( ) ) ) this.invalidate( ); this.stopInteracting( ); } if ( fVelocityTracker != null ) { fVelocityTracker.recycle( ); fVelocityTracker = null; } fTranslatePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_DOWN: { break; } case MotionEvent.ACTION_POINTER_UP: { final int pointerIndex = (event.getAction( ) & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = event.getPointerId( pointerIndex ); if ( pointerId == fTranslatePointerId ) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; fTranslateLastTouch.set( event.getX( newPointerIndex ), event.getY( newPointerIndex ) ); fTranslatePointerId = event.getPointerId( newPointerIndex ); } break; } case MotionEvent.ACTION_CANCEL: { if ( fScroller.springBack( this.getScrollX( ), this.getScrollY( ), 0, fWorksheetWidth - this.getWidth( ), 0, fWorksheetHeight - this.getHeight( ) ) ) this.invalidate( ); fTranslatePointerId = INVALID_POINTER_ID; break; } } return true; } private void fling(int velocityX, int velocityY) { int x = this.getScrollX( ); int y = this.getScrollY( ); this.startInteracting( ); //fScroller.setFriction( ViewConfiguration.getScrollFriction( ) ); fScroller.fling( x, y, velocityX, velocityY, 0, fWorksheetWidth - this.getWidth( ), 0, fWorksheetHeight - this.getHeight( ) ); this.invalidate( ); } private void startInteracting() { fInteracting = true; } private void stopInteracting() { fInteracting = false; } @Override public void computeScroll() { if ( fScroller != null && fScroller.computeScrollOffset( ) ) { int oldX = this.getScrollX( ); int oldY = this.getScrollY( ); int x = fScroller.getCurrX( ); int y = fScroller.getCurrY( ); if ( oldX != x || oldY != y ) { this.overScrollBy( x - oldX, y - oldY, oldX, oldY, fWorksheetWidth - this.getWidth( ), fWorksheetHeight - this.getHeight( ), OVERSCROLL_DISTANCE, OVERSCROLL_DISTANCE, false ); } if ( fScroller.isFinished( ) ) this.stopInteracting( ); this.postInvalidate( ); } } @Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { // Treat animating scrolls differently; see #computeScroll() for why. if ( !fScroller.isFinished( ) ) { super.scrollTo( scrollX, scrollY ); if ( clampedX || clampedY ) { fScroller.springBack( this.getScrollX( ), this.getScrollY( ), 0, fWorksheetWidth - this.getWidth( ), 0, fWorksheetHeight - this.getHeight( ) ); } } else { super.scrollTo( scrollX, scrollY ); } awakenScrollBars( ); } @Override protected int computeHorizontalScrollExtent() { return this.getWidth( ); } @Override protected int computeHorizontalScrollRange() { return fWorksheetWidth; } @Override protected int computeHorizontalScrollOffset() { return this.getScrollX( ); } @Override protected int computeVerticalScrollExtent() { return this.getHeight( ); } @Override protected int computeVerticalScrollRange() { return fWorksheetHeight; } @Override protected int computeVerticalScrollOffset() { return this.getScrollY( ); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor( Color.BLACK ); Paint paint = new Paint( ); if ( fInteracting ) ; paint.setColor( Color.WHITE ); canvas.drawRect( 0, 0, fWorksheetWidth, fWorksheetHeight, paint ); paint.setColor( Color.RED ); for (int i = 0; i < 1500; i += 10) { canvas.drawLine( i, 0, i + 100, 500, paint ); } canvas.drawRect( fWorksheetWidth - 50, 0, fWorksheetWidth, fWorksheetHeight, paint ); canvas.drawRect( 0, fWorksheetHeight - 50, fWorksheetWidth, fWorksheetHeight, paint ); } } 

Bueno, tengo exactamente el mismo problema.

Miré la implementación de la vista de android, y dentro de 'onOverScrolled' sólo está el comentario "Intencionalmente vacío".

En cuanto a la implementación de ListView, puedo ver que lo han implementado manualmente obteniendo los elementos desplegables 'com.android.internal.R.styleable.ListView_overScrollHeader' y 'com.android.internal.R.styleable.ListView_overScrollFooter' que no pueden ser Utilizado desde fuera. Desafortunadamente, no pude encontrar esos recursos en las fuentes.

Ya que parece que tendremos que hacer overscroll manualmente …

Ok, para responder a la pregunta de Android estilo overscroll. Usted puede ir un paso más allá de mi última respuesta (el código) Después de usar OverScroller para implementar overscrolling a través de dibujo, puede restringir el movimiento de la página, por lo que la página no overscroll. En su lugar, puede dibujar un recurso para el estilo de Android overscroll – y eso es lo que usted llama "manual".

Vea el ejemplo a continuación, que es además del código que posteé ayer. Ahora, el 'onDraw' no permitirá que la página visualmente sobrecargue. Entonces 'displatchDraw' dibujará un recurso dibujable para representar el estilo de Android overscroll.

Nota, soy un monstruo de la memoria, así que utilizo el mismo recurso para las cuatro esquinas y lo giro manualmente a través de matrices al renderizar en el lienzo.

  private void compensateForOverscroll(Canvas canvas) { int x = this.getScrollX( ); int y = this.getScrollY( ); Matrix matrix = canvas.getMatrix( ); int maxX = fWorksheetWidth - this.getWidth( ); int maxY = fWorksheetHeight - this.getHeight( ); if ( x < 0 || x > maxX || y < 0 || y > maxY ) { if ( x < 0 ) matrix.postTranslate( x, 0 ); else if ( x > maxX ) matrix.postTranslate( (x - maxX), 0 ); if ( y < 0 ) matrix.postTranslate( 0, y ); else if ( y > maxY ) matrix.postTranslate( 0, (y - maxY) ); canvas.setMatrix( matrix ); } } @Override protected void dispatchDraw(Canvas canvas) { int width = this.getWidth( ); int height = this.getHeight( ); int maxX = fWorksheetWidth - width; int maxY = fWorksheetHeight - height; int x = this.getScrollX( ); int y = this.getScrollY( ); if ( x < 0 || x > maxX ) { canvas.save( ); Matrix canvasMatrix = canvas.getMatrix( ); if ( x < 0 ) { fOverScrollDrawable.setBounds( 0, x, height, x - x ); canvasMatrix.preRotate( -90 ); canvasMatrix.preTranslate( - y - height, 0 ); } else if ( x > maxX ) { fOverScrollDrawable.setBounds( 0, maxX, height, x ); canvasMatrix.preRotate( 90 ); canvasMatrix.preTranslate( y, - x - fWorksheetWidth ); } canvas.setMatrix( canvasMatrix ); fOverScrollDrawable.draw( canvas ); canvas.restore( ); } if ( y < 0 || y > maxY ) { canvas.save( ); Matrix canvasMatrix = canvas.getMatrix( ); if ( y < 0 ) { fOverScrollDrawable.setBounds( x, y, x + width, y - y ); } else if ( y > maxY ) { fOverScrollDrawable.setBounds( 0, maxY, width, y ); canvasMatrix.preRotate( 180 ); canvasMatrix.preTranslate( - x - width, - y - fWorksheetHeight ); } canvas.setMatrix( canvasMatrix ); fOverScrollDrawable.draw( canvas ); canvas.restore( ); } } @Override protected void onDraw(Canvas canvas) { canvas.save( ); this.compensateForOverscroll( canvas ); canvas.drawColor( Color.BLACK ); Paint paint = new Paint( ); if ( fInteracting ) ; paint.setColor( Color.WHITE ); canvas.drawRect( 0, 0, fWorksheetWidth, fWorksheetHeight, paint ); paint.setColor( Color.RED ); for (int i = 0; i < 1500; i += 10) { canvas.drawLine( i, 0, i + 100, 500, paint ); } canvas.drawRect( fWorksheetWidth - 50, 0, fWorksheetWidth, fWorksheetHeight, paint ); canvas.drawRect( 0, fWorksheetHeight - 50, fWorksheetWidth, fWorksheetHeight, paint ); canvas.restore( ); } 

El dibujable se inicializa así:

 Drawable fOverScrollDrawable; ... Resources rsr = context.getResources( ); fOverScrollDrawable = rsr.getDrawable( R.drawable.overscroll_glow ); 

El recurso de imagen para el resplandor de overscroll, he tomado dos imágenes en el sdk y los ha combinado en un único recurso extraíble:

  • \ Android-sdk \ plataformas \ android-11 \ data \ res \ drawable-hdpi \ overscroll_edge.png
  • \ Android-sdk \ plataformas \ android-11 \ data \ res \ drawable-hdpi \ overscroll_glow.png
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.