2010-08-12 490 views
15

我正在研究一個具有主屏幕的應用程序。這個主屏幕應該像安卓主屏,您可以通過觸摸屏上的手指在幾個視圖之間切換。開發Android主屏幕

該解決方案很簡單。我有3視圖實例,,當前視圖。我從之前初始化的viewflipper中獲得這些實例。由於我有一臺HTC G1屏幕,寬度爲320像素,高度爲480像素。

想象一下,當您觸摸屏幕時,您會捕捉動作向下動作事件的向下值。然後你移動你的手指,屏幕應該以完全相同的方式移動,所以你必須重新計算視圖的位置。它迄今爲止適用於我,但我面臨一個奇怪的問題。當您在不移動手指的情況下觸摸右側視圖,但將其保持在屏幕上時,視圖會消失並顯示左側視圖。

這裏是我的代碼:

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

} 

我覺得這樣的主屏幕是一個有趣的UI元素。

任何想法?


您也可以下載工作Eclipse項目在這裏:因爲似乎有

http://www.megaupload.com/?d=3M3IYGGM

+0

你可以重新加載ur eclipse項目,因爲megaupload.com被解僱了嗎? – Kalpesh 2012-07-03 13:26:34

回答

23

編輯(2012年7月3日)對於這個答案仍然有很多觀點和評論,我想我應該添加一個註釋,即使用較新的SDK,你現在應該使用ViewPager而不是具有相同的功能。該類還包含在Android Support library中,因此您還可以使用它在早期的Android設備上運行。

編輯(2013年3月4日):

因爲還有人來這裏,就是想也說我把一個ViewPager與背景在較慢的速度移動給視差效果。代碼是here

如果你真的想手工做這一切,原來答案就在這裏...以下

我認爲你可以找到你所尋找的位置:http://www.anddev.org/why_do_not_these_codes_work-t4012.html

我曾經在不同的項目也可以創建具有不同視圖的主屏幕。這是直接從Android啓動器,它遵循該線程後很好地工作。

這是我的代碼...第一源代碼

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

而且佈局文件:

<?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> 

爲了能夠在XML文件中的額外屬性,要保存這個在res /價值/ attrs.xml

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

您好,我遇到了一些與此有關的問題。它對我來說運行不好,我沒有錯誤。該應用程序剛剛顯示與力關閉按鈕。你可以上傳一個工作示例.. – 2010-11-23 15:17:53

+0

該代碼本身在我的應用程序中工作正常。你可以上傳日誌或使用pastebin嗎? – Matthieu 2010-11-23 16:28:53

+0

這裏是日誌http://pastebin.com/GAS1irqZ – 2010-11-23 18:41:49

4
+1

請參閱http://stackoverflow.com/questions/2501307/horizo​​ntal-tabish-scroll-between-views其他關於andro-views的看法 – 2011-05-11 18:16:57

2

Matthieu的代碼非常好,但它在方向更改後並不保存狀態。要解決此問題,請將下一個代碼添加到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]; 
     } 
     }; 
    } 

我已經從凱文的答案中提取了它。