2010-08-12 16 views
15

Je travaille sur une application qui a un écran d'accueil. Cet écran d'accueil devrait se comporter comme le écran d'accueil android où vous pouvez basculer entre plusieurs vues en jetant votre doigt sur l'écran tactile.Développement d'un écran d'accueil Android

La solution est facile. J'ai 3 vues instances, droite, gauche et vue actuelle. Je reçois ces instances du viewflipper que j'ai initialisé plus tôt. Depuis que j'ai un HTC G1 mon écran a 320 px de largeur et 480 px de hauteur. Imaginez que vous capturiez la valeur d'un événement de mouvement vers le bas lorsque vous touchez l'écran. Ensuite, vous bougez votre doigt et l'écran devrait se déplacer exactement de la même manière, vous devez donc recalculer la position de la vue. Cela fonctionne pour moi jusqu'à présent mais je suis confronté à un problème étrange. Lorsque vous touchez la vue de droite sans bouger votre doigt mais en le gardant sur l'écran pendant moins d'une seconde, la vue disparaît et montre la vue de gauche.

Voici mon code:

public class MainActivity extends Activity implements OnTouchListener{ 

    private ViewFlipper vf; 
    private float downXValue; 
    private View view1, view2, view3; 

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

     this.vf = (ViewFlipper) findViewById(R.id.flipper); 

     if(this.vf != null){ 
      this.view1 = vf.getChildAt(0); 
      this.view2 = vf.getChildAt(1); 
      this.view3 = vf.getChildAt(2); 
      vf.setDisplayedChild(0); 
     }  

     LinearLayout layMain = (LinearLayout) findViewById(R.id.layout_main); 
     layMain.setOnTouchListener((OnTouchListener) this); 
    } 

    public boolean onTouch(View v, MotionEvent arg1) { 

     final View currentView = vf.getCurrentView(); 
     final View leftView, rightView; 

     if(currentView == view1){ 
      leftView = view3; 
      rightView = view2; 
     }else if(currentView == view2){ 
      leftView = view1; 
      rightView = view3; 
     }else if(currentView == view3){ 
      leftView = view2; 
      rightView = view1; 
     }else{ 
      leftView = null; 
      rightView = null; 
     } 

     switch (arg1.getAction()){ 
      case MotionEvent.ACTION_DOWN:{ 
       this.downXValue = arg1.getX(); 
       break; 
      } 
      case MotionEvent.ACTION_UP:{ 
       float currentX = arg1.getX();    
        if ((downXValue < currentX)){ 
         if(currentView != view3){ 
         float t3 = (320-(currentX-downXValue))/320;        
         this.vf.setInAnimation(AnimationHelper.inFromLeftAnimation(t3)); 
         this.vf.setOutAnimation(AnimationHelper.outToRightAnimation(t3)); 
         this.vf.showPrevious(); } 
         } 

        if ((downXValue > currentX)){ 
         if(currentView != view2){ 
         float t = (320-(downXValue-currentX))/320; 
         this.vf.setInAnimation(AnimationHelper.inFromRightAnimation(t)); 
         this.vf.setOutAnimation(AnimationHelper.outToLeftAnimation(t)); 
         this.vf.showNext();}  
        }       
      } 
      break; 
      case MotionEvent.ACTION_MOVE:{ 

       leftView.setVisibility(View.VISIBLE); 
       rightView.setVisibility(View.VISIBLE); 

       float currentX = arg1.getX();  
       if(downXValue > currentX){ 
        if(currentView != view2){ 
         currentView.layout((int) (currentX - downXValue), 
         currentView.getTop(), 
         (int) (currentX - downXValue) + 320, 
         currentView.getBottom()); 
        } 
       } 

       if(downXValue < currentX){ 
        if(currentView != view3){ 
         currentView.layout((int) (currentX - downXValue), 
         currentView.getTop(), 
         (int) (currentX - downXValue) + 320, 
         currentView.getBottom()); 


        } 
       } 
       leftView.layout(currentView.getLeft()-320, leftView.getTop(), 
         currentView.getLeft(), leftView.getBottom()); 

       rightView.layout(currentView.getRight(), rightView.getTop(), 
         currentView.getRight() + 320, rightView.getBottom()); 
       } 
      } 

     return true; 
    } 

    public static class AnimationHelper { 
      public static Animation inFromRightAnimation(float param) { 
      Animation inFromRight = new TranslateAnimation(
      Animation.RELATIVE_TO_PARENT, +param, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f); 
      inFromRight.setDuration(250); 
      inFromRight.setInterpolator(new AccelerateInterpolator()); 
      return inFromRight; 
      } 

      public static Animation outToLeftAnimation(float param) { 
      Animation outtoLeft = new TranslateAnimation(
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, -param, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f); 
      outtoLeft.setDuration(250); 
      outtoLeft.setInterpolator(new AccelerateInterpolator()); 
      return outtoLeft; 
      } 

      // for the next movement 
      public static Animation inFromLeftAnimation(float param) { 
      Animation inFromLeft = new TranslateAnimation(
      Animation.RELATIVE_TO_PARENT, -param, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f); 
      inFromLeft.setDuration(250); 
      inFromLeft.setInterpolator(new AccelerateInterpolator()); 
      return inFromLeft; 
      } 

      public static Animation outToRightAnimation(float param) { 
      Animation outtoRight = new TranslateAnimation(
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, +param, 
      Animation.RELATIVE_TO_PARENT, 0.0f, 
      Animation.RELATIVE_TO_PARENT, 0.0f); 
      outtoRight.setDuration(250); 
      outtoRight.setInterpolator(new AccelerateInterpolator()); 
      return outtoRight; 
      } 
     } 

} 

Je pense qu'un tel écran d'accueil est un élément d'interface utilisateur intéressante.

Des idées?


Vous pouvez télécharger le projet Eclipse de travail ici:http://www.megaupload.com/?d=3M3IYGGM

+0

Vous pouvez renvoyer votre projet eclipse parce que megaupload.com est fermé? – Kalpesh

Répondre

23

EDIT (3rd Juillet 2012) Fréquence:

Comme il semble être encore un certain nombre de points de vue et de commentaires sur cette réponse, je pensais que je devrais ajouter une note, que, avec le nouveau SDK, vous devriez maintenant utiliser ViewPager au lieu d'avoir la même fonctionnalité. Cette classe est également incluse dans le Android Support library afin que vous puissiez également l'utiliser pour exécuter sur les appareils Android plus anciens.

EDIT (4ème Mars, 2013):

Comme il y a encore des gens qui viennent ici, je voulais juste dire aussi que je mets ensemble un ViewPager avec l'arrière-plan se déplaçant à une vitesse plus lente pour donner un effet de parallaxe. Le code est here.

Si vous voulez vraiment faire tout manuellement, la réponse originale est ici-bas ...

Je pense que vous pouvez trouver ce que vous cherchez ici: http://www.anddev.org/why_do_not_these_codes_work-t4012.html

J'ai utilisé que dans un autre projet de créer également un écran d'accueil avec des vues différentes. Ceci est directement à partir du lanceur Android, cela fonctionne très bien après avoir suivi ce fil.

Voici mon code ...d'abord le code source

package com.matthieu.launcher; 

import android.content.Context; 
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; 

public class DragableSpace 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 DragableSpace(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)); 
    } 

    public DragableSpace(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     mScroller = new Scroller(context); 

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

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

     TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.DragableSpace); 
     mCurrentScreen = a.getInteger(R.styleable.DragableSpace_default_screen, 0); 
    } 

    @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; 

       if (xMoved) { 
        // Scroll if the user moved far enough along the X axis 
        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: 
       // Log.i(LOG_TAG,"event : move"); 
       // if (mTouchState == TOUCH_STATE_SCROLLING) { 
       // Scroll to follow the motion event 
       final int deltaX = (int) (mLastMotionX - x); 
       mLastMotionX = x; 

       //Log.i(LOG_TAG, "event : move, deltaX " + deltaX + ", mScrollX " + mScrollX); 

       if (deltaX < 0) { 
        if (mScrollX > 0) { 
         scrollBy(Math.max(-mScrollX, deltaX), 0); 
        } 
       } else if (deltaX > 0) { 
        final int availableToScroll = getChildAt(getChildCount() - 1) 
         .getRight() 
         - mScrollX - getWidth(); 
        if (availableToScroll > 0) { 
         scrollBy(Math.min(availableToScroll, deltaX), 0); 
        } 
       } 
       // } 
       break; 
      case MotionEvent.ACTION_UP: 
       Log.i(LOG_TAG, "event : up"); 
       // if (mTouchState == TOUCH_STATE_SCROLLING) { 
       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; 
       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; 
     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; 
     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(); 
     } 
    } 
} 

Et le fichier de mise en page:

<?xml version="1.0" encoding="utf-8"?> 
<com.matthieu.launcher.DragableSpace xmlns:app="http://schemas.android.com/apk/res/com.matthieu.launcher" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/space" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
app:default_screen="1" 
> 
<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" /> 
</com.matthieu.launcher.DragableSpace> 

Pour être en mesure d'avoir l'attribut supplémentaire dans le fichier xml, vous souhaitez enregistrer cela dans res/valeurs/attrs.xml

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

Salut à nouveau, j'ai quelques problèmes avec cet impementation. Ca ne fonctionne pas bien pour moi, je n'ai pas d'erreur. L'application apparaît juste avec le bouton de fermeture forcée. pouvez-vous télécharger un exemple de travail s'il vous plaît .. –

+0

Ce code à lui seul fonctionne très bien dans mon application. Pouvez-vous télécharger le journal dans pastebin ou quelque chose comme ça? – Matthieu

+0

voici le log http://pastebin.com/GAS1irqZ –

4
+1

voir http://stackoverflow.com/questions/2501307/horizontal-tabish-scroll-between-views pour les autres commentaires sur andro-views –

2

Le code de Matthieu est très bon, mais il ne sauvegarde pas l'état après changement d'orientation. Pour résoudre ce problème, ajoutez le code suivant à la classe DragableSpace

/** 
    * 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; 
     } 
    } 

    // ========================= 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]; 
     } 
     }; 
    } 

Je l'ai pris à partir de la réponse de Kevin.