Android Open Source - draggable-panel-layout Dragged Panel Layout






From Project

Back to project page draggable-panel-layout.

License

The source code is released under:

Apache License

If you think the Android project draggable-panel-layout listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2013 Alexander Osmanov/*from  ww  w.ja  va 2 s .c  o  m*/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package com.evilduck.animtest;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;

public class DraggedPanelLayout extends FrameLayout {

    private static final float PARALLAX_FACTOR = 0.2f;

    private static DecelerateInterpolator sDecelerator = new DecelerateInterpolator();

    private float parallaxFactor;

    private int bottomPanelPeekHeight;

    private float touchY;

    private boolean touching;

    private boolean opened = false;

    private VelocityTracker velocityTracker = null;

    private View bottomPanel;

    private View slidingPanel;

    private Drawable shadowDrawable;

    private boolean animating = false;

    private boolean willDrawShadow = false;

    private int touchSlop;

    private boolean isBeingDragged = false;

    public DraggedPanelLayout(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);

  initAttrs(context, attrs);
    }

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

  initAttrs(context, attrs);
    }

    public DraggedPanelLayout(Context context) {
  super(context);

  bottomPanelPeekHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources()
    .getDisplayMetrics());
  parallaxFactor = PARALLAX_FACTOR;

  if (!isInEditMode()) {
      shadowDrawable = getResources().getDrawable(R.drawable.shadow_np);
      willDrawShadow = true;
  }

  touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    public void initAttrs(Context context, AttributeSet attrs) {
  TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DraggedPanelLayout, 0, 0);

  try {
      parallaxFactor = a.getFloat(R.styleable.DraggedPanelLayout_parallax_factor, PARALLAX_FACTOR);
      if (parallaxFactor < 0.1 || parallaxFactor > 0.9) {
    parallaxFactor = PARALLAX_FACTOR;
      }

      int defaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources()
        .getDisplayMetrics());
      bottomPanelPeekHeight = a.getDimensionPixelSize(R.styleable.DraggedPanelLayout_bottom_panel_height,
        defaultHeight);
      int shadowDrawableId = a.getResourceId(R.styleable.DraggedPanelLayout_shadow_drawable, -1);
      if (shadowDrawableId != -1) {
    shadowDrawable = getResources().getDrawable(shadowDrawableId);
    willDrawShadow = true;
    setWillNotDraw(!willDrawShadow);
      }
  } finally {
      a.recycle();
  }

  final ViewConfiguration configuration = ViewConfiguration.get(getContext());
  touchSlop = configuration.getScaledTouchSlop();
    }

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

  if (!isInEditMode() && willDrawShadow) {
      int top = (int) (slidingPanel.getTop() + slidingPanel.getTranslationY());
      shadowDrawable.setBounds(0, top - shadowDrawable.getIntrinsicHeight(), getMeasuredWidth(), top);
      shadowDrawable.draw(canvas);

  }
  if (animating) {
      ViewCompat.postInvalidateOnAnimation(DraggedPanelLayout.this);
  }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  super.onLayout(changed, left, top, right, bottom);

  if (getChildCount() != 2) {
      throw new IllegalStateException("DraggedPanelLayout must have 2 children!");
  }

  bottomPanel = getChildAt(0);
  bottomPanel.layout(left, top, right, bottom - bottomPanelPeekHeight);

  slidingPanel = getChildAt(1);
  if (!opened) {
      int panelMeasuredHeight = slidingPanel.getMeasuredHeight();
      slidingPanel.layout(left, bottom - bottomPanelPeekHeight, right, bottom - bottomPanelPeekHeight
        + panelMeasuredHeight);
  }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
      touchY = event.getY();
  } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
      if (Math.abs(touchY - event.getY()) > touchSlop) {
    isBeingDragged = true;
    startDragging(event);
      }
  } else if (event.getAction() == MotionEvent.ACTION_UP) {
      isBeingDragged = false;
  }

  return isBeingDragged;
    }

    public void startDragging(MotionEvent event) {
  touchY = event.getY();
  touching = true;

  bottomPanel.setVisibility(View.VISIBLE);

  obtainVelocityTracker();
  velocityTracker.addMovement(event);
  allowShadow();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
  obtainVelocityTracker();

  if (event.getAction() == MotionEvent.ACTION_DOWN) {
      startDragging(event);
  } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
      if (touching) {
    velocityTracker.addMovement(event);

    float translation = event.getY() - touchY;
    translation = boundTranslation(translation);

    slidingPanel.setTranslationY(translation);
    bottomPanel
      .setTranslationY((float) (opened ? -(getMeasuredHeight() - bottomPanelPeekHeight - translation)
        * parallaxFactor : translation * parallaxFactor));

    if (willDrawShadow) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
      }
  } else if (event.getAction() == MotionEvent.ACTION_UP) {
      isBeingDragged = false;
      touching = false;

      velocityTracker.addMovement(event);
      velocityTracker.computeCurrentVelocity(1);
      float velocityY = velocityTracker.getYVelocity();
      velocityTracker.recycle();
      velocityTracker = null;

      finishAnimateToFinalPosition(velocityY);
  }

  return true;
    }

    public float boundTranslation(float translation) {
  if (!opened) {
      if (translation > 0) {
    translation = 0;
      }
      if (Math.abs(translation) >= slidingPanel.getMeasuredHeight() - bottomPanelPeekHeight) {
    translation = -slidingPanel.getMeasuredHeight() + bottomPanelPeekHeight;
      }
  } else {
      if (translation < 0) {
    translation = 0;
      }
      if (translation >= slidingPanel.getMeasuredHeight() - bottomPanelPeekHeight) {
    translation = slidingPanel.getMeasuredHeight() - bottomPanelPeekHeight;
      }
  }
  return translation;
    }

    public void obtainVelocityTracker() {
  if (velocityTracker == null) {
      velocityTracker = VelocityTracker.obtain();
  }
    }

    public void finishAnimateToFinalPosition(float velocityY) {
  final boolean flinging = Math.abs(velocityY) > 0.5;

  boolean opening;
  float distY;
  long duration;

  if (flinging) {
      // If fling velocity is fast enough we continue the motion starting
      // with the current speed

      opening = velocityY < 0;

      distY = calculateDistance(opening);
      duration = Math.abs(Math.round(distY / velocityY));

      animatePanel(opening, distY, duration);
  } else {
      // If user motion is slow or stopped we check if half distance is
      // traveled and based on that complete the motion

      boolean halfway = Math.abs(slidingPanel.getTranslationY()) >= (getMeasuredHeight() - bottomPanelPeekHeight) / 2;
      opening = opened ? !halfway : halfway;

      distY = calculateDistance(opening);
      duration = Math.round(300 * (double) Math.abs((double) slidingPanel.getTranslationY())
        / (double) (getMeasuredHeight() - bottomPanelPeekHeight));

  }

  animatePanel(opening, distY, duration);
    }

    public float calculateDistance(boolean opening) {
  float distY;
  if (opened) {
      distY = opening ? -slidingPanel.getTranslationY() : getMeasuredHeight() - bottomPanelPeekHeight
        - slidingPanel.getTranslationY();
  } else {
      distY = opening ? -(getMeasuredHeight() - bottomPanelPeekHeight + slidingPanel.getTranslationY())
        : -slidingPanel.getTranslationY();
  }

  return distY;
    }

    public void animatePanel(final boolean opening, float distY, long duration) {
  ObjectAnimator slidingPanelAnimator = ObjectAnimator.ofFloat(slidingPanel, View.TRANSLATION_Y,
    slidingPanel.getTranslationY(), slidingPanel.getTranslationY() + distY);
  ObjectAnimator bottomPanelAnimator = ObjectAnimator.ofFloat(bottomPanel, View.TRANSLATION_Y,
    bottomPanel.getTranslationY(), bottomPanel.getTranslationY() + (float) (distY * parallaxFactor));

  AnimatorSet set = new AnimatorSet();
  set.playTogether(slidingPanelAnimator, bottomPanelAnimator);
  set.setDuration(duration);
  set.setInterpolator(sDecelerator);
  set.addListener(new MyAnimListener(opening));
  set.start();
    }

    class MyAnimListener implements AnimatorListener {

  int oldLayerTypeOne;

  int oldLayerTypeTwo;

  boolean opening;

  public MyAnimListener(boolean opening) {
      super();
      this.opening = opening;
  }

  @Override
  public void onAnimationStart(Animator animation) {
      oldLayerTypeOne = slidingPanel.getLayerType();
      oldLayerTypeOne = bottomPanel.getLayerType();

      slidingPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
      bottomPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);

      bottomPanel.setVisibility(View.VISIBLE);

      if (willDrawShadow) {
    animating = true;
    ViewCompat.postInvalidateOnAnimation(DraggedPanelLayout.this);
      }
  }

  @Override
  public void onAnimationRepeat(Animator animation) {
  }

  @Override
  public void onAnimationEnd(Animator animation) {
      setOpenedState(opening);

      bottomPanel.setTranslationY(0);
      slidingPanel.setTranslationY(0);

      slidingPanel.setLayerType(oldLayerTypeOne, null);
      bottomPanel.setLayerType(oldLayerTypeTwo, null);

      requestLayout();

      if (willDrawShadow) {
    animating = false;
    ViewCompat.postInvalidateOnAnimation(DraggedPanelLayout.this);
      }
  }

  @Override
  public void onAnimationCancel(Animator animation) {
      if (willDrawShadow) {
    animating = false;
    ViewCompat.postInvalidateOnAnimation(DraggedPanelLayout.this);
      }
  }

    };

    private void setOpenedState(boolean opened) {
  this.opened = opened;
  bottomPanel.setVisibility(opened ? View.GONE : View.VISIBLE);
  hideShadowIfNotNeeded();
    }

    private void allowShadow() {
  willDrawShadow = shadowDrawable != null;
  setWillNotDraw(!willDrawShadow);
    }

    private void hideShadowIfNotNeeded() {
  willDrawShadow = shadowDrawable != null && !opened;
  setWillNotDraw(!willDrawShadow);
    }

}




Java Source Code List

com.evilduck.animtest.DraggedPanelLayout.java
com.evilduck.animtest.MainActivity.java