2011-01-23 6 views
14

quello che voglio avere è un'immagine di sfondo, che si comporta come lo stock homescreen o il tempo app qui: https://youtube.com/watch?v=i2Oh4GL5wBE#t=0m54sAndroid: immagine di sfondo in movimento, mentre la navigazione attraverso Visualizzazioni

Ho solo bisogno di un immagine di sfondo non animato (come il strada in quel video) che scorre "un po '" mentre si passa a un'altra vista. Nella mia app, vorrei scorrere alcune ListViews con uno sfondo a scorrimento.

Quello che ho provato:

  • ViewFlipper: Ho usato grande immagine e tagliare verticalmente in 5 pezzi. Quindi ho impostato le immagini tagliate in ogni layout (visualizzando ListViews) come sfondo.
  • HorizontalScrollView: In HorizontalScrollView ho aggiunto un LinearLayout e ho impostato l'immagine grande come sfondo. Quindi ho usato GestureDectector per aggiungere "snap" quando si scorreva attraverso ListViews.

Entrambi hanno funzionato, ma ciò richiede un'immagine molto grande e non sono sicuro se si adatta correttamente alle diverse dimensioni dello schermo. Inoltre non si comporta come la homescreen originale, in cui lo sfondo si sposta solo un po 'mentre il primo piano cambia alla vista successiva.

ho letto il post qui: https://stackoverflow.com/questions/2943619/android-make-application-background-behave-like-homescreen-background

ho controllato il Launcher.java raccomandata che mi ha portato alla Workspace.java:

https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/Workspace.java

Per quanto ho capito, utilizza il WallpaperManager impostando Offset.

mWallpaperManager.setWallpaperOffsets(getWindowToken(), 
       Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); 

API dice:

setWallpaperOffsets (IBinder windowToken, galleggiante xOffset, galleggiante YOffset) impostare la posizione del sfondo corrente all'interno di qualsiasi spazio più ampio , quando tale sfondo è visibile dietro la finestra data.

Ma per quanto ho capito, questo cambia solo l'immagine di sfondo del telefono stesso.

Cosa posso fare? Conosce un'applicazione open source o un codice di esempio che fa lo stesso? Probabilmente devo disegnare Canvas da solo (mai fatto prima). Per favore, dammi un consiglio

risposta

10

Infine, ho funzionato (ancora bisogno di alcune modifiche). :)

Devo ammettere che non ero in grado di disegnare lo sfondo nello stesso ViewGroup. Bene, ha funzionato, ma non ho potuto controllare lo scorrimento (scorreva troppo). Quello che ho praticamente fatto, è stato l'unione di 2 tutorial.

  • prima da qui: Android Homescreen Si passa tra diversi layout. Quindi, prima di tutto, ho impostato sfondi trasparenti.
  • Secondo uno da qui: Draggable Symbols Trascina i simboli sullo schermo tramite il tocco (utilizzando onDraw, come suggerito). Ho ridotto i simboli a uno solo e l'ho modificato da ImageView a LinearLayout.

In questo layout ho messo il ViewGroup, ha fatto alcuni piccoli cambiamenti nella onTouchEvent e onInterceptTouchEvent, ha aggiunto un certo codice male e alla fine ha funzionato. :)

Se qualcuno è interessato, pulirò il codice e lo posterò qui, ma mi vergogno del mio stile di codifica, è un casino. ;)

Grazie mille, ho apprezzato il tuo aiuto! :)

UPDATE: Quindi, ecco il codice:

MainActivity.java

package de.android.projects; 

import android.app.Activity; 
import android.os.Bundle; 

public class MainActivity extends Activity { 

    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
    } 
} 

MoveBackground.java

package de.android.projects; 

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.drawable.Drawable; 
import android.util.AttributeSet; 
import android.widget.LinearLayout; 

/* 
* LinearLayout which moves the background image by touchEvents. 
* Taken from: http://eagle.phys.utk.edu/guidry/android/DraggableSymbols.html 
* and changed a little bit 
*/ 

public class MoveBackground extends LinearLayout{ 

    private Drawable background;  // background picture 
    private float X = -300;    // Current x coordinate, upper left corner - 300 
    private int scroll; 

    public MoveBackground(Context context) { 
     super(context); 
    } 

    public MoveBackground(Context context, AttributeSet attrs) { 
     super(context); 

     background = context.getResources().getDrawable(R.drawable.backgroundpicture); 
//just for tests, not really optimized yet :) 
    background.setBounds(0,0,1000,getResources().getDisplayMetrics().heightPixels); 

     setWillNotDraw(false); 
    } 

    /* 
    * Don't need these methods, maybe later for gesture improvements 
    */ 
    /* 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     onTouchEvent(ev); 
     return false; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     final int action = ev.getAction(); 

     switch (action) { 
      // MotionEvent class constant signifying a finger-drag event 
      case MotionEvent.ACTION_MOVE: { 
       // Request a redraw 
       invalidate(); 
       break; 
      } 
      // MotionEvent class constant signifying a finger-up event 
      case MotionEvent.ACTION_UP: 
       invalidate(); // Request redraw 
       break; 
     } 
     return true; 
    } 
    */ 


    // This method will be called each time the screen is redrawn. 
    // When to redraw is under Android control, but we can request a redraw 
    // using the method invalidate() inherited from the View superclass. 

    @Override 
    public void onDraw(Canvas canvas) { 
     super.onDraw(canvas);  

    // get the object movement 
     if (BadScrollHelp.getScrollX() != scroll){ 
      //reduce the scrolling 
      X -= scroll/5; 
      scroll = BadScrollHelp.getScrollX(); 
     } 

     // Draw background image at its current locations 
     canvas.save(); 
     canvas.translate(X,0); 
     background.draw(canvas); 
     canvas.restore(); 
    } 
} 

ViewFlip per.java

package de.android.projects; 

import android.content.Context; 
import android.os.Parcel; 
import android.os.Parcelable; 
import android.util.Log; 
import android.content.res.TypedArray; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.VelocityTracker; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.ViewConfiguration; 
import android.widget.Scroller; 

/* 
* Flip different views. Taken from tutorial there http://android-projects.de/2011/01/04/android-homescreen-view-flipper/ 
*/ 

public class ViewFlipper extends ViewGroup { 
    private Scroller mScroller; 
    private VelocityTracker mVelocityTracker; 

    private int mScrollX = 0; 
    private int mCurrentScreen = 0; 

    private float mLastMotionX; 

    private static final String LOG_TAG = "DragableSpace"; 

    private static final int SNAP_VELOCITY = 1000; 

    private final static int TOUCH_STATE_REST = 0; 
    private final static int TOUCH_STATE_SCROLLING = 1; 

    private int mTouchState = TOUCH_STATE_REST; 

    private int mTouchSlop = 0; 

    public ViewFlipper(Context context) { 
     super(context); 
     mScroller = new Scroller(context); 

     mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 

     this.setLayoutParams(new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT, 
        ViewGroup.LayoutParams.FILL_PARENT)); 

     setWillNotDraw(false); 
     requestDisallowInterceptTouchEvent(true); 
    } 


    public ViewFlipper(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DragableSpace); 
     mCurrentScreen = a.getInteger(R.styleable.DragableSpace_default_screen, 0); 

     mScroller = new Scroller(context); 

     mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 

     this.setLayoutParams(new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT , 
        ViewGroup.LayoutParams.FILL_PARENT)); 

     setWillNotDraw(false); 
     requestDisallowInterceptTouchEvent(true); 
    } 


    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
     /* 
     * This method JUST determines whether we want to intercept the motion. 
     * If we return true, onTouchEvent will be called and we do the actual 
     * scrolling there. 
     */ 

     /* 
     * Shortcut the most recurring case: the user is in the dragging state 
     * and he is moving his finger. We want to intercept this motion. 
     */ 
     final int action = ev.getAction(); 
     if ((action == MotionEvent.ACTION_MOVE) 
       && (mTouchState != TOUCH_STATE_REST)) { 
      return true; 
       } 

     final float x = ev.getX(); 

     switch (action) { 
      case MotionEvent.ACTION_MOVE: 
       /* 
       * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 
       * whether the user has moved far enough from his original down touch. 
       */ 

       /* 
       * Locally do absolute value. mLastMotionX is set to the y value 
       * of the down event. 
       */ 
       final int xDiff = (int) Math.abs(x - mLastMotionX); 
       boolean xMoved = xDiff > mTouchSlop + 50; 

       if (xMoved) { 
        // Scroll if the user moved far enough along the X axis then 
        mTouchState = TOUCH_STATE_SCROLLING; 
       } 
       break; 

      case MotionEvent.ACTION_DOWN: 
       // Remember location of down touch 
       mLastMotionX = x; 
       /* 
       * If being flinged and user touches the screen, initiate drag; 
       * otherwise don't. mScroller.isFinished should be false when 
       * being flinged. 
       */ 
       mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; 
       break; 

      case MotionEvent.ACTION_CANCEL: 
      case MotionEvent.ACTION_UP: 
       // Release the drag 
       mTouchState = TOUCH_STATE_REST; 
       break; 
     } 

     /* 
     * The only time we want to intercept motion events is if we are in the 
     * drag mode. 
     */ 
     return mTouchState != TOUCH_STATE_REST; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     if (mVelocityTracker == null) { 
      mVelocityTracker = VelocityTracker.obtain(); 
     } 
     mVelocityTracker.addMovement(event); 

     final int action = event.getAction(); 
     final float x = event.getX(); 

     switch (action) { 
      case MotionEvent.ACTION_DOWN: 
       Log.i(LOG_TAG, "event : down"); 
       /* 
       * If being flinged and user touches, stop the fling. isFinished 
       * will be false if being flinged. 
       */ 
       if (!mScroller.isFinished()) { 
        mScroller.abortAnimation(); 
       } 

       // Remember where the motion event started 
       mLastMotionX = x; 

       break; 
      case MotionEvent.ACTION_MOVE: 
       // Scroll to follow the motion event 
       final int deltaX = (int) (mLastMotionX - x); 
       mLastMotionX = x; 

       if (deltaX < 0) { 
        if (mScrollX > 0) { 
         BadScrollHelp.setScrollX(deltaX); 
         scrollBy(Math.max(-mScrollX, deltaX), 0); 
        } 
       } else if (deltaX > 0) { 
        final int availableToScroll = getChildAt(getChildCount() - 1) 
         .getRight() 
         - mScrollX - getWidth(); 
        if (availableToScroll > 0) { 
         BadScrollHelp.setScrollX(deltaX); 
         scrollBy(Math.min(availableToScroll, deltaX), 0); 
        } 
       } 

       // Request a redraw 
       invalidate(); 
       break; 
      case MotionEvent.ACTION_UP: 
       Log.i(LOG_TAG, "event : up"); 
       final VelocityTracker velocityTracker = mVelocityTracker; 
       velocityTracker.computeCurrentVelocity(1000); 
       int velocityX = (int) velocityTracker.getXVelocity(); 

       if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { 
        // Fling hard enough to move left 
        snapToScreen(mCurrentScreen - 1); 
       } else if (velocityX < -SNAP_VELOCITY 
         && mCurrentScreen < getChildCount() - 1) { 
        // Fling hard enough to move right 
        snapToScreen(mCurrentScreen + 1); 
       } else { 
        snapToDestination(); 
       } 

       if (mVelocityTracker != null) { 
        mVelocityTracker.recycle(); 
        mVelocityTracker = null; 
       } 
       mTouchState = TOUCH_STATE_REST; 
       //neu unten 
       invalidate(); // Request redraw 
       break; 
      case MotionEvent.ACTION_CANCEL: 
       Log.i(LOG_TAG, "event : cancel"); 
       mTouchState = TOUCH_STATE_REST; 
     } 
     mScrollX = this.getScrollX(); 

     return true; 
    } 

    private void snapToDestination() { 
     final int screenWidth = getWidth(); 
     final int whichScreen = (mScrollX + (screenWidth/2))/screenWidth; 
     Log.i(LOG_TAG, "from des"); 
     snapToScreen(whichScreen); 
    } 

    public void snapToScreen(int whichScreen) {   
     Log.i(LOG_TAG, "snap To Screen " + whichScreen); 
     mCurrentScreen = whichScreen; 
     BadScrollHelp.setCurrentScreen(mCurrentScreen); 
     final int newX = whichScreen * getWidth(); 
     final int delta = newX - mScrollX; 
     mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2); 

     invalidate(); 
    } 

    public void setToScreen(int whichScreen) { 
     Log.i(LOG_TAG, "set To Screen " + whichScreen); 
     mCurrentScreen = whichScreen; 
     BadScrollHelp.setCurrentScreen(mCurrentScreen); 
     final int newX = whichScreen * getWidth(); 
     mScroller.startScroll(newX, 0, 0, 0, 10);    
     invalidate(); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     int childLeft = 0; 

     final int count = getChildCount(); 
     for (int i = 0; i < count; i++) { 
      final View child = getChildAt(i); 
      if (child.getVisibility() != View.GONE) { 
       final int childWidth = child.getMeasuredWidth(); 
       child.layout(childLeft, 0, childLeft + childWidth, child 
         .getMeasuredHeight()); 
       childLeft += childWidth; 
      } 
     } 

    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     final int width = MeasureSpec.getSize(widthMeasureSpec); 
     final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
     if (widthMode != MeasureSpec.EXACTLY) { 
      //throw new IllegalStateException("error mode."); 
     } 

     final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
     if (heightMode != MeasureSpec.EXACTLY) { 
      //throw new IllegalStateException("error mode."); 
     } 

     // The children are given the same width and height as the workspace 
     final int count = getChildCount(); 
     for (int i = 0; i < count; i++) { 
      getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); 
     } 
     Log.i(LOG_TAG, "moving to screen "+mCurrentScreen); 
     scrollTo(mCurrentScreen * width, 0);  
    } 

    @Override 
    public void computeScroll() { 
     if (mScroller.computeScrollOffset()) { 
      mScrollX = mScroller.getCurrX(); 
      scrollTo(mScrollX, 0); 
      postInvalidate(); 
     } 
    } 

    /** 
    * Return the parceable instance to be saved 
    */ 
    @Override 
    protected Parcelable onSaveInstanceState() { 
     final SavedState state = new SavedState(super.onSaveInstanceState()); 
     state.currentScreen = mCurrentScreen; 
     return state; 
    } 


    /** 
    * Restore the previous saved current screen 
    */ 
    @Override 
    protected void onRestoreInstanceState(Parcelable state) { 
     SavedState savedState = (SavedState) state; 
     super.onRestoreInstanceState(savedState.getSuperState()); 
     if (savedState.currentScreen != -1) { 
     mCurrentScreen = savedState.currentScreen; 
     BadScrollHelp.setCurrentScreen(mCurrentScreen); 
     } 
    } 

    // ========================= INNER CLASSES ============================== 

    public interface onViewChangedEvent{  
     void onViewChange (int currentViewIndex); 
    } 

    /** 
    * A SavedState which save and load the current screen 
    */ 
    public static class SavedState extends BaseSavedState { 
     int currentScreen = -1; 

     /** 
     * Internal constructor 
     * 
     * @param superState 
     */ 
     SavedState(Parcelable superState) { 
     super(superState); 
     } 

     /** 
     * Private constructor 
     * 
     * @param in 
     */ 
     private SavedState(Parcel in) { 
     super(in); 
     currentScreen = in.readInt(); 
     } 

     /** 
     * Save the current screen 
     */ 
     @Override 
     public void writeToParcel(Parcel out, int flags) { 
     super.writeToParcel(out, flags); 
     out.writeInt(currentScreen); 
     } 

     /** 
     * Return a Parcelable creator 
     */ 
     public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { 
     public SavedState createFromParcel(Parcel in) { 
      return new SavedState(in); 
     } 

     public SavedState[] newArray(int size) { 
      return new SavedState[size]; 
     } 
     }; 
    } 
} 

BadScrollHelp.java

package de.android.projects; 

public class BadScrollHelp { 
    private static int scrollX = 0; 
    private static int currentScreen = 0; 

    public static synchronized void setScrollX(int scroll){ 
     scrollX = scroll; 
    } 

    public static synchronized void setCurrentScreen(int screen){ 
     currentScreen = screen; 
    } 

    public static synchronized int getScrollX(){ 
     return scrollX; 
    } 

    public static synchronized int getCurrentScreen(){ 
     return currentScreen; 
    } 

} 

main.xml

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical"> 

    <de.android.projects.MoveBackground 
     xmlns:app="http://schemas.android.com/apk/res/de.android.projects" 
     android:id="@+id/space1" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" > 

     <de.android.projects.ViewFlipper 
      xmlns:app="http://schemas.android.com/apk/res/de.android.projects" 
      android:id="@+id/space" 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" 
      app:default_screen="0" > 
      <include android:id="@+id/left" layout="@layout/left_screen" /> 
      <include android:id="@+id/center" layout="@layout/initial_screen" /> 
      <include android:id="@+id/right" layout="@layout/right_screen" /> 
     </de.android.projects.ViewFlipper> 

    </de.android.projects.MoveBackground> 

</FrameLayout> 

left_screen.xml, right_screen.xml e initial_screen.xml

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:background="#00000000" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 

    <LinearLayout android:layout_width="fill_parent" 
       android:layout_height="fill_parent" 
       android:orientation="vertical" > 

     <Button android:layout_width="100dip" 
       android:layout_height="50dip" 
       android:text="Button" /> 

    </LinearLayout> 

</LinearLayout> 

attrs.xml (nella cartella valori)

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <declare-styleable name="DragableSpace"> 
     <attr name="default_screen" format="integer"/> 
    </declare-styleable> 
</resources> 

Così, finalmente, il gioco è fatto. Il metodo onDraw richiede ancora alcune modifiche. Non sono soddisfatto di questa soluzione fino ad ora, perché deve essere possibile utilizzare il metodo onDraw nello ViewGroup, penso. Ma non riuscivo a capirlo. Inoltre, l'impostazione di variabili con metodi statici sembra essere un trucco sporco.

Sarebbe felice se qualcuno potesse darmi consigli su come passare quegli eventi dallo ViewFlipper alla classe madre MoveBackground. O come includere il metodo di disegno MoveBackground nello ViewFlipper.

È possibile integrare la classe ViewFlipper nella classe MoveBackground e fare un addView(viewFlipper) a livello di programmazione. Di quanto non avrei più bisogno di questa soluzione statica. :)

soluzione
+0

Sarebbe bello vedere il codice, solo per avere qualche idea. Sto pensando di avere uno sfondo animato dietro un layout standard, ma sto ancora cercando l'approccio migliore. – Lumis

+1

Bene, ho modificato il mio post recente sopra. – Rainer

+0

Sembra buono, tuttavia mi piacerebbe provare prima il codice per vedere cosa può fare. Quanto è grande la tua immagine di sfondo? E puoi pubblicare R.styleable.DragableSpace e DragableSpace_default_screen come quello che manca ... Exlipse si lamenta anche dell'app: default_screen = "0" in main.xml – Lumis

5

Il modo più semplice per ottenere ciò è disegnare l'immagine sul Canvas passato a onDraw(Canvas). Questo è ciò che usava Launcher prima di introdurre l'API di offset della carta da parati.

+0

OK. Pensavo di dover andare in questo modo. Ci proverò domani. Grazie :) – Rainer

+0

Quindi la domanda è possibile aggiungere una normale vista su una tela disegnata? – Lumis

+1

Sì, è possibile, basta usare un ViewGroup. –

0

Molto utile Rainer, grazie mille, questo è stato veramente utile, ma ho notato che

Drawable.draw(canvas) 

funziona a essere molto lento rispetto al

Canvas.drawBitmap(bitmap,srcRect,dstRect,mPaint) 

provarlo!

10

Kind of vecchia questione ...

ho implementato qualcosa di simile, anche ignorando onDraw nel ViewPager.

Ho postato tutto il codice here

+0

C'è un modo per farlo, ma con due immagini? –

+0

Vuoi dire avere un'immagine accanto a un'altra? Sono sicuro che è possibile cambiare il codice per farlo ... – Matthieu

Problemi correlati