2012-12-03 12 views
5

enter image description hereAndroidでスライドレイアウトアニメーションを作成する方法は?

こんにちは、「画像」に示すようにマイアクティビティでスライドアニメーションを作成します。 Moreボタンをクリックすると、オレンジのレイアウトが白の上にスライダーのように表示され、Lessボタンをクリックするとスライダーのように下がるはずです。

このようなアプローチを作成するための最良の方法については、サンプルコードまたはチュートリアルをお勧めします。

ありがとうございます!

答えて

2

これを使用してPanel classこれがあなたの要件を満たし、イベントを処理することもできます。

参考:あなたが持っているあなたの活動にこれを再生するにhttp://code.google.com/p/android-misc-widgets/

Panel.java

public class Panel extends LinearLayout { 

    /** 
    * Callback invoked when the panel is opened/closed. 
    */ 
    public static interface OnPanelListener { 
     /** 
     * Invoked when the panel becomes fully closed. 
     */ 
     public void onPanelClosed(Panel panel); 
     /** 
     * Invoked when the panel becomes fully opened. 
     */ 
     public void onPanelOpened(Panel panel); 
    } 

    private boolean mIsShrinking; 
    private int mPosition; 
    private int mDuration; 
    private boolean mLinearFlying; 
    private int mHandleId; 
    private int mContentId; 
    private View mHandle; 
    private View mContent; 
    private Drawable mOpenedHandle; 
    private Drawable mClosedHandle; 
    private float mTrackX; 
    private float mTrackY; 
    private float mVelocity; 

    private OnPanelListener panelListener; 

    public static final int TOP = 0; 
    public static final int BOTTOM = 1; 
    public static final int LEFT = 2; 
    public static final int RIGHT = 3; 

    private enum State { 
     ABOUT_TO_ANIMATE, 
     ANIMATING, 
     READY, 
     TRACKING, 
     FLYING, 
    }; 
    private State mState; 
    private Interpolator mInterpolator; 
    private GestureDetector mGestureDetector; 
    private int mContentHeight; 
    private int mContentWidth; 
    private int mOrientation; 
    private float mWeight; 
    private PanelOnGestureListener mGestureListener; 
    private boolean mBringToFront; 

    public Panel(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel); 
     mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750);  // duration defaults to 750 ms 
     mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM);   // position defaults to BOTTOM 
     mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false); // linearFlying defaults to false 
     mWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f);   // weight defaults to 0.0 
     if (mWeight < 0 || mWeight > 1) { 
      mWeight = 0.0f; 
      //Log.w(TAG, a.getPositionDescription() + ": weight must be > 0 and <= 1"); 
     } 
     mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle); 
     mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle); 

     RuntimeException e = null; 
     mHandleId = a.getResourceId(R.styleable.Panel_handle, 0); 
     if (mHandleId == 0) { 
      e = new IllegalArgumentException(a.getPositionDescription() + 
        ": The handle attribute is required and must refer to a valid child."); 
     } 
     mContentId = a.getResourceId(R.styleable.Panel_content, 0); 
     if (mContentId == 0) { 
      e = new IllegalArgumentException(a.getPositionDescription() + 
        ": The content attribute is required and must refer to a valid child."); 
     } 
     a.recycle(); 

     if (e != null) { 
      throw e; 
     } 
     mOrientation = (mPosition == TOP || mPosition == BOTTOM)? VERTICAL : HORIZONTAL; 
     setOrientation(mOrientation); 
     mState = State.READY; 
     mGestureListener = new PanelOnGestureListener(); 
     mGestureDetector = new GestureDetector(mGestureListener); 
     mGestureDetector.setIsLongpressEnabled(false); 

     // i DON'T really know why i need this... 
     setBaselineAligned(false); 
    } 

    /** 
    * Sets the listener that receives a notification when the panel becomes open/close. 
    * 
    * @param onPanelListener The listener to be notified when the panel is opened/closed. 
    */ 
    public void setOnPanelListener(OnPanelListener onPanelListener) { 
     panelListener = onPanelListener; 
    } 

    /** 
    * Gets Panel's mHandle 
    * 
    * @return Panel's mHandle 
    */ 
    public View getHandle() { 
     return mHandle; 
    } 

    /** 
    * Gets Panel's mContent 
    * 
    * @return Panel's mContent 
    */ 
    public View getContent() { 
     return mContent; 
    } 


    /** 
    * Sets the acceleration curve for panel's animation. 
    * 
    * @param i The interpolator which defines the acceleration curve 
    */ 
    public void setInterpolator(Interpolator i) { 
     mInterpolator = i; 
    } 

    /** 
    * Set the opened state of Panel. 
    * 
    * @param open True if Panel is to be opened, false if Panel is to be closed. 
    * @param animate True if use animation, false otherwise. 
    * 
    * @return True if operation was performed, false otherwise. 
    * 
    */ 
    public boolean setOpen(boolean open, boolean animate) { 
     if (mState == State.READY && isOpen()^open) { 
      mIsShrinking = !open; 
      if (animate) { 
       mState = State.ABOUT_TO_ANIMATE; 
       if (!mIsShrinking) { 
        // this could make flicker so we test mState in dispatchDraw() 
        // to see if is equal to ABOUT_TO_ANIMATE 
        mContent.setVisibility(VISIBLE); 
       } 
       post(startAnimation); 
      } else { 
       mContent.setVisibility(open? VISIBLE : GONE); 
       postProcess(); 
      } 
      return true; 
     } 
     return false; 
    } 

    /** 
    * Returns the opened status for Panel. 
    * 
    * @return True if Panel is opened, false otherwise. 
    */ 
    public boolean isOpen() { 
     return mContent.getVisibility() == VISIBLE; 
    } 

    @Override 
    protected void onFinishInflate() { 
     super.onFinishInflate(); 
     mHandle = findViewById(mHandleId); 
     if (mHandle == null) { 
      String name = getResources().getResourceEntryName(mHandleId); 
      throw new RuntimeException("Your Panel must have a child View whose id attribute is 'R.id." + name + "'"); 
     } 
     mHandle.setOnTouchListener(touchListener); 
     mHandle.setOnClickListener(clickListener); 

     mContent = findViewById(mContentId); 
     if (mContent == null) { 
      String name = getResources().getResourceEntryName(mHandleId); 
      throw new RuntimeException("Your Panel must have a child View whose id attribute is 'R.id." + name + "'"); 
     } 

     // reposition children 
     removeView(mHandle); 
     removeView(mContent); 
     if (mPosition == TOP || mPosition == LEFT) { 
      addView(mContent); 
      addView(mHandle); 
     } else { 
      addView(mHandle); 
      addView(mContent); 
     } 

     if (mClosedHandle != null) { 
      mHandle.setBackgroundDrawable(mClosedHandle); 
     } 
     mContent.setClickable(true); 
     mContent.setVisibility(GONE); 
     if (mWeight > 0) { 
      ViewGroup.LayoutParams params = mContent.getLayoutParams(); 
      if (mOrientation == VERTICAL) { 
       params.height = ViewGroup.LayoutParams.FILL_PARENT; 
      } else { 
       params.width = ViewGroup.LayoutParams.FILL_PARENT; 
      } 
      mContent.setLayoutParams(params); 
     } 
    } 

    @Override 
    protected void onAttachedToWindow() { 
     super.onAttachedToWindow(); 
     ViewParent parent = getParent(); 
     if (parent != null && parent instanceof FrameLayout) { 
      mBringToFront = true; 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     if (mWeight > 0 && mContent.getVisibility() == VISIBLE) { 
      View parent = (View) getParent(); 
      if (parent != null) { 
       if (mOrientation == VERTICAL) { 
        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (parent.getHeight() * mWeight), MeasureSpec.EXACTLY); 
       } else { 
        widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (parent.getWidth() * mWeight), MeasureSpec.EXACTLY); 
       } 
      } 
     } 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     super.onLayout(changed, l, t, r, b); 
     mContentWidth = mContent.getWidth(); 
     mContentHeight = mContent.getHeight(); 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
//  String name = getResources().getResourceEntryName(getId()); 
//  //Log.d(TAG, name + " ispatchDraw " + mState); 
     // this is why 'mState' was added: 
     // avoid flicker before animation start 
     if (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking) { 
      int delta = mOrientation == VERTICAL? mContentHeight : mContentWidth; 
      if (mPosition == LEFT || mPosition == TOP) { 
       delta = -delta; 
      } 
      if (mOrientation == VERTICAL) { 
       canvas.translate(0, delta); 
      } else { 
       canvas.translate(delta, 0); 
      } 
     } 
     if (mState == State.TRACKING || mState == State.FLYING) { 
      canvas.translate(mTrackX, mTrackY); 
     } 
     super.dispatchDraw(canvas); 
    } 

    private float ensureRange(float v, int min, int max) { 
     v = Math.max(v, min); 
     v = Math.min(v, max); 
     return v; 
    } 

    OnTouchListener touchListener = new OnTouchListener() { 
     int initX; 
     int initY; 
     boolean setInitialPosition; 
     public boolean onTouch(View v, MotionEvent event) { 
      if (mState == State.ANIMATING) { 
       // we are animating 
       return false; 
      } 
//   //Log.d(TAG, "state: " + mState + " x: " + event.getX() + " y: " + event.getY()); 
      int action = event.getAction(); 
      if (action == MotionEvent.ACTION_DOWN) { 
       if (mBringToFront) { 
        bringToFront(); 
       } 
       initX = 0; 
       initY = 0; 
       if (mContent.getVisibility() == GONE) { 
        // since we may not know content dimensions we use factors here 
        if (mOrientation == VERTICAL) { 
         initY = mPosition == TOP? -1 : 1; 
        } else { 
         initX = mPosition == LEFT? -1 : 1; 
        } 
       } 
       setInitialPosition = true; 
      } else { 
       if (setInitialPosition) { 
        // now we know content dimensions, so we multiply factors... 
        initX *= mContentWidth; 
        initY *= mContentHeight; 
        // ... and set initial panel's position 
        mGestureListener.setScroll(initX, initY); 
        setInitialPosition = false; 
        // for offsetLocation we have to invert values 
        initX = -initX; 
        initY = -initY; 
       } 
       // offset every ACTION_MOVE & ACTION_UP event 
       event.offsetLocation(initX, initY); 
      } 
      if (!mGestureDetector.onTouchEvent(event)) { 
       if (action == MotionEvent.ACTION_UP) { 
        // tup up after scrolling 
        post(startAnimation); 
       } 
      } 
      return false; 
     } 
    }; 

    OnClickListener clickListener = new OnClickListener() { 
     public void onClick(View v) { 
      if (mBringToFront) { 
       bringToFront(); 
      } 
      if (initChange()) { 
       post(startAnimation); 
      } 
     } 
    }; 

    public boolean initChange() { 
     if (mState != State.READY) { 
      // we are animating or just about to animate 
      return false; 
     } 
     mState = State.ABOUT_TO_ANIMATE; 
     mIsShrinking = mContent.getVisibility() == VISIBLE; 
     if (!mIsShrinking) { 
      // this could make flicker so we test mState in dispatchDraw() 
      // to see if is equal to ABOUT_TO_ANIMATE 
      mContent.setVisibility(VISIBLE); 
     } 
     return true; 
    } 

    Runnable startAnimation = new Runnable() { 
     public void run() { 

      callPanListener(); 

      // this is why we post this Runnable couple of lines above: 
      // now its save to use mContent.getHeight() && mContent.getWidth() 
      TranslateAnimation animation; 
      int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0; 
      if (mState == State.FLYING) { 
       mIsShrinking = (mPosition == TOP || mPosition == LEFT)^(mVelocity > 0); 
      } 
      int calculatedDuration; 
      if (mOrientation == VERTICAL) { 
       int height = mContentHeight; 
       if (!mIsShrinking) { 
        fromYDelta = mPosition == TOP? -height : height; 
       } else { 
        toYDelta = mPosition == TOP? -height : height; 
       } 
       if (mState == State.TRACKING) { 
        if (Math.abs(mTrackY - fromYDelta) < Math.abs(mTrackY - toYDelta)) { 
         mIsShrinking = !mIsShrinking; 
         toYDelta = fromYDelta; 
        } 
        fromYDelta = (int) mTrackY; 
       } else 
       if (mState == State.FLYING) { 
        fromYDelta = (int) mTrackY; 
       } 
       // for FLYING events we calculate animation duration based on flying velocity 
       // also for very high velocity make sure duration >= 20 ms 
       if (mState == State.FLYING && mLinearFlying) { 
        calculatedDuration = (int) (1000 * Math.abs((toYDelta - fromYDelta)/mVelocity)); 
        calculatedDuration = Math.max(calculatedDuration, 20); 
       } else { 
        calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta)/mContentHeight; 
       } 
      } else { 
       int width = mContentWidth; 
       if (!mIsShrinking) { 
        fromXDelta = mPosition == LEFT? -width : width; 
       } else { 
        toXDelta = mPosition == LEFT? -width : width; 
       } 
       if (mState == State.TRACKING) { 
        if (Math.abs(mTrackX - fromXDelta) < Math.abs(mTrackX - toXDelta)) { 
         mIsShrinking = !mIsShrinking; 
         toXDelta = fromXDelta; 
        } 
        fromXDelta = (int) mTrackX; 
       } else 
       if (mState == State.FLYING) { 
        fromXDelta = (int) mTrackX; 
       } 
       // for FLYING events we calculate animation duration based on flying velocity 
       // also for very high velocity make sure duration >= 20 ms 
       if (mState == State.FLYING && mLinearFlying) { 
        calculatedDuration = (int) (1000 * Math.abs((toXDelta - fromXDelta)/mVelocity)); 
        calculatedDuration = Math.max(calculatedDuration, 20); 
       } else { 
        calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta)/mContentWidth; 
       } 
      } 

      mTrackX = mTrackY = 0; 
      if (calculatedDuration == 0) { 
       mState = State.READY; 
       if (mIsShrinking) { 
        mContent.setVisibility(GONE); 
       } 
       postProcess(); 
       return; 
      } 

      animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); 
      animation.setDuration(calculatedDuration); 
      if (mState == State.FLYING && mLinearFlying) { 
       animation.setInterpolator(new LinearInterpolator()); 
      } else 
      if (mInterpolator != null) { 
       animation.setInterpolator(mInterpolator); 
      } 
      startAnimation(animation); 
     } 
    }; 

    @Override 
    protected void onAnimationEnd() { 
     super.onAnimationEnd(); 
     mState = State.READY; 
     if (mIsShrinking) { 
      mContent.setVisibility(GONE); 
     } 
     postProcess(); 
    } 

    @Override 
    protected void onAnimationStart() { 
     super.onAnimationStart(); 
     mState = State.ANIMATING; 
    } 

    private void postProcess() { 
     if (mIsShrinking && mClosedHandle != null) { 
      mHandle.setBackgroundDrawable(mClosedHandle); 
     } else 
     if (!mIsShrinking && mOpenedHandle != null) { 
      mHandle.setBackgroundDrawable(mOpenedHandle); 
     } 
     // invoke listener if any 
     callPanListener(); 
    } 
    public void callPanListener() 
    { 
     if (panelListener != null) { 
      if (mIsShrinking) { 
       panelListener.onPanelClosed(Panel.this); 
      } else { 
       panelListener.onPanelOpened(Panel.this); 
      } 
     } 
    } 

    class PanelOnGestureListener implements OnGestureListener { 
     float scrollY; 
     float scrollX; 
     public void setScroll(int initScrollX, int initScrollY) { 
      scrollX = initScrollX; 
      scrollY = initScrollY; 
     } 
     public boolean onDown(MotionEvent e) { 
      scrollX = scrollY = 0; 
      callPanListener(); 
      initChange(); 
      return true; 
     } 
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
      mState = State.FLYING; 
//   velocityX=400; 
//   velocityY=400; 
      mVelocity = mOrientation == VERTICAL? velocityY : velocityX; 
//   mVelocity=400; 
      post(startAnimation); 
      return true; 
     } 
     public void onLongPress(MotionEvent e) { 
      // not used 
     } 
     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
      mState = State.TRACKING; 
      float tmpY = 0, tmpX = 0; 
      if (mOrientation == VERTICAL) { 
       scrollY -= distanceY; 
       if (mPosition == TOP) { 
        tmpY = ensureRange(scrollY, -mContentHeight, 0); 
       } else { 
        tmpY = ensureRange(scrollY, 0, mContentHeight); 
       } 
      } else { 
       scrollX -= distanceX; 
       if (mPosition == LEFT) { 
        tmpX = ensureRange(scrollX, -mContentWidth, 0); 
       } else { 
        tmpX = ensureRange(scrollX, 0, mContentWidth); 
       } 
      } 
      if (tmpX != mTrackX || tmpY != mTrackY) { 
       mTrackX = tmpX; 
       mTrackY = tmpY; 
       invalidate(); 
      } 
      return true; 
     } 
     public void onShowPress(MotionEvent e) { 
      // not used 
     } 
     public boolean onSingleTapUp(MotionEvent e) { 
      // not used 
      return false; 
     } 
    } 
} 

main.xml

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

<LinearLayout 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:layout_alignParentBottom="true" 
     android:layout_centerHorizontal="true" 
     android:gravity="center_horizontal" > 

     <packagename.Panel 
      android:id="@+id/panel_menu" 
      android:layout_width="fill_parent" 
      android:layout_height="fill_parent" 
      panel:animationDuration="1000" 
      panel:closedHandle="#0000FF" 
      panel:content="@+id/panelContent" 
      panel:handle="@+id/panelHandle" 
      panel:linearFlying="false" 
      panel:openedHandle="#0000FF" 
      android:paddingTop="4dip" 
      panel:position="bottom" > 

      <Button 
       android:id="@+id/panelHandle" 
       android:layout_width="33dp" 
       android:layout_height="33dp" 
       android:layout_gravity="center_horizontal" 
       android:text="^" 
       android:textColor="@android:color/white" /> 

      <RelativeLayout 
       android:id="@+id/panelContent" 
       android:layout_width="fill_parent" 
       android:layout_height="50dp" 
       android:background="#0000FF" > 
      </RelativeLayout> 
     </packagename.Panel> 
</RelativeLayout> 

attrs.xml

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <declare-styleable name="Panel"> 
     <!-- Defines panel animation duration in ms. --> 
     <attr name="animationDuration" format="integer" /> 
     <!-- Defines panel position on the screen. --> 
     <attr name="position"> 
      <!-- Panel placed at top of the screen. --> 
      <enum name="top" value="0" /> 
      <!-- Panel placed at bottom of the screen. --> 
      <enum name="bottom" value="1" /> 
      <!-- Panel placed at left of the screen. --> 
      <enum name="left" value="2" /> 
      <!-- Panel placed at right of the screen. --> 
      <enum name="right" value="3" /> 
     </attr> 
     <!-- Identifier for the child that represents the panel's handle. --> 
     <attr name="handle" format="reference" /> 
     <!-- Identifier for the child that represents the panel's content. --> 
     <attr name="content" format="reference" /> 
     <!-- Defines if flying gesture forces linear interpolator in animation. --> 
     <attr name="linearFlying" format="boolean" /> 
     <!-- Defines size relative to parent (must be in form: nn%p). --> 
     <attr name="weight" format="fraction" /> 
     <!-- Defines opened handle (drawable/color). --> 
     <attr name="openedHandle" format="reference|color" /> 
     <!-- Defines closed handle (drawable/color). --> 
     <attr name="closedHandle" format="reference|color" /> 
    </declare-styleable> 

</resources> 

SampleActivity.java

public class SampleActivity extends Activity implements OnPanelListener { 

public Panel samplePanel; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.panel_main)_layout; 

     samplePanel = (Panel) findViewById(R.id.panel_id); 
     samplePanel.setOnPanelListener(this); 
     samplePanel.setInterpolator(new ExpoInterpolator(Type.OUT)); 

    } 

    public void onPanelClosed(Panel panel) {}//Interface Listener 
    public void onPanelOpened(Panel panel) {}//Interface Listener 
} 
+0

あなたは、少なくともあなたが作成者にいくつかの認識を与えるために、コードをコピーした場所からリポジトリへのリンクを掲載している可能性があります。 – Luksprog

+0

こんにちは@hotveryspicyあなたの努力をいただきありがとうございます。しかし、あなたは私に、samplePanel.setInterpolator(new ExpoInterpolator(Type.OUT))の行にあるExpoInterpolatorを教えてください。 –

+0

アニメーションの種類で、パネルの開閉方法に影響します。 –