Sviluppo di una homescreen Android

Sto lavorando a un’app con una schermata iniziale. Questa homescreen dovrebbe comportarsi come la schermata iniziale di Android in cui è ansible passare da una visualizzazione all’altra toccando il dito sullo schermo tattile.

La soluzione è facile. Ho 3 istanze di vista , destra , sinistra e vista corrente . Ottengo questa istanza dal viewflipper che ho inizializzato in precedenza. Poiché ho un HTC G1, il mio sreen è di 320 px in larghezza e 480 px in altezza.

Immagina di catturare il valore negativo di un evento di movimento verso il basso quando tocchi lo schermo. Quindi muovi il dito e lo schermo dovrebbe muoversi esattamente allo stesso modo, quindi devi ricalcolare la posizione della vista. Funziona per me finora ma sto affrontando uno strano problema. Quando si tocca la vista destra senza muovere il dito ma mantenendolo sullo schermo per meno di un secondo, la vista scompare e mostra la vista sinistra.

Ecco il mio codice:

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 != 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; } } } 

Penso che un Homescreen sia un elemento dell’interfaccia utente interessante.

Qualche idea?


Puoi scaricare qui il progetto eclipse funzionante: http://www.megaupload.com/?d=3M3IYGGM

EDIT (3 luglio 2012):

Dal momento che sembrano esserci ancora parecchie opinioni e commenti su questa risposta, ho pensato di aggiungere una nota, che con il nuovo SDK dovresti ora utilizzare ViewPager per avere la stessa funzionalità. Quella class è anche inclusa nella libreria di supporto Android in modo da poterla utilizzare anche per i dispositivi Android precedenti.

EDIT (4 marzo 2013):

Dato che ci sono ancora persone che vengono qui, volevo solo dire che ho messo insieme un ViewPager con lo sfondo che si sposta a bassa velocità per dare un effetto di parallasse. Il codice è qui .

Se vuoi davvero fare tutto manualmente, la risposta originale è qui sotto …

Penso che tu possa trovare quello che stai cercando qui: http://www.anddev.org/why_do_not_these_codes_work-t4012.html

L’ho usato in un altro progetto per creare anche una schermata iniziale con visualizzazioni diverse. Questo è direttamente da Android Launcher, funziona abbastanza bene dopo aver seguito quella discussione.

Ecco il mio codice … prima il codice sorgente

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

E il file di layout:

 < ?xml version="1.0" encoding="utf-8"?>      

Per poter avere l'attributo extra nel file xml, devi salvarlo in res / values ​​/ attrs.xml

 < ?xml version="1.0" encoding="utf-8"?>      

Potresti consultare http://code.google.com/p/andro-view /

Il codice di Matthieu è molto buono, ma non salva lo stato dopo l’orientamento modificato. Per risolvere questo problema, aggiungi il prossimo codice alla class 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 CREATOR = new Parcelable.Creator() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } 

l’ho preso dalla risposta di Kevin.