com.github.ppamorim.cult.CultView.java Source code

Java tutorial

Introduction

Here is the source code for com.github.ppamorim.cult.CultView.java

Source

/*
* Copyright (C) 2015 Pedro Paulo de Amorim
*
* 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.github.ppamorim.cult;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.dd.ShadowLayout;
import com.github.ppamorim.cult.util.AnimationHelper;
import com.github.ppamorim.cult.util.ViewUtil;

/**
 * This is the main class of the library
 * here is inflated some views and added
 * some params to it. After is added a
 * isntance os CultHelperCallback to
 * provide a drag possibility to CultView
 * (not working yet)
 *
 * @author Pedro Paulo Amorim
 *
 */
public class CultView extends FrameLayout {

    private static final int INVALID_POINTER = -1;
    private static final int DEFAULT_HEIGHT = 64;
    private static final int DEFAULT_PADDING = 0;
    private static final float DEFAULT_DRAG_LIMIT = 0.5f;
    private static final float SENSITIVITY = 1.0f;

    private boolean isAnimationRunning;

    private int toolbarHeight;
    private int innerPadding;
    private int contentResId;
    private int activePointerId = INVALID_POINTER;
    private float verticalDragRange;

    private ShadowLayout shadowLayout;
    private FrameLayout innerView;
    private Toolbar innerToolbar;
    private FrameLayout contentInner;
    private Toolbar outToolbar;
    private FrameLayout contentOut;
    private View shadow;

    private CultHelperCallback cultHelperCallback;
    private ViewDragHelper dragHelper;
    private AnimationHelper animationHelper;

    public CultView(Context context) {
        this(context, null);
    }

    public CultView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CultView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getStyle(context, attrs, defStyleAttr);
    }

    /**
     * Override method to map some views and to configure the
     * view height, animation and to initialize DragViewHelper.
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (!isInEditMode()) {
            config();
            configSizes();
            configDragViewHelper();
            configSlideHelper();
        }
    }

    /**
     * Force to measure the #contentOut view
     *
     * @param widthMeasureSpec provide the width of this view based on pixels
     * @param heightMeasureSpec provide the height of this view based on pixels
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measureWidth = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                MeasureSpec.EXACTLY);
        int measureHeight = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                MeasureSpec.EXACTLY);
        if (contentOut != null) {
            contentOut.measure(measureWidth, measureHeight);
        }
    }

    /**
     *  When the system verify that is needed to
     *  measure the view, was added a method to
     *  set the vertical drag range based on
     *  the height of the view
     */
    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        super.onSizeChanged(width, height, oldWidth, oldHeight);
        setVerticalDragRange(height);
    }

    /**
     * To ensure the animation is going to work this method has been override to call
     * postInvalidateOnAnimation if the view is not settled yet.
     */
    @Override
    public void computeScroll() {
        if (!isInEditMode() && dragHelper != null && dragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    /**
     * Override method to intercept only touch events over the drag view and to cancel the drag when
     * the action associated to the MotionEvent is equals to ACTION_CANCEL or ACTION_UP.
     *
     * @param ev captured.
     * @return true if the view is going to process the touch event or false if not.
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (!isEnabled()) {
            return false;
        }
        switch (MotionEventCompat.getActionMasked(ev)) {
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            dragHelper.cancel();
            return false;
        case MotionEvent.ACTION_DOWN:
            int index = MotionEventCompat.getActionIndex(ev);
            activePointerId = MotionEventCompat.getPointerId(ev, index);
            if (activePointerId == INVALID_POINTER) {
                return false;
            }
        default:
            dragHelper.processTouchEvent(ev);
            return dragHelper.shouldInterceptTouchEvent(ev);
        }
    }

    /**
     * Override method to dispatch touch
     * event to the dragged view.
     *
     * @param ev captured.
     * @return true if the touch event is realized over the drag.
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int actionMasked = MotionEventCompat.getActionMasked(ev);
        if ((actionMasked & MotionEventCompat.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
            activePointerId = MotionEventCompat.getPointerId(ev, actionMasked);
        }
        if (activePointerId == INVALID_POINTER) {
            return false;
        }
        dragHelper.processTouchEvent(ev);
        return ViewUtil.isViewHit(this, contentOut, (int) ev.getX(), (int) ev.getY());
    }

    /**
     * Configure the instance of DragViewHelper
     */
    public void configDragViewHelper() {
        cultHelperCallback = new CultHelperCallback(this, contentOut);
        dragHelper = ViewDragHelper.create(this, SENSITIVITY, cultHelperCallback);
    }

    /**
     * This method inflate the base view and
     * instantiate some layouts based on
     * this view
     *
     * @return instance of CultView
     */
    private CultView config() {
        addView(inflate(getContext(), R.layout.layout_cult, null));
        shadowLayout = (ShadowLayout) findViewById(R.id.shadow_layout);
        innerView = (FrameLayout) findViewById(R.id.inner_view);
        innerToolbar = (Toolbar) findViewById(R.id.inner_toolbar);
        outToolbar = (Toolbar) findViewById(R.id.out_toolbar);
        contentOut = (FrameLayout) findViewById(R.id.content_out);
        contentInner = (FrameLayout) findViewById(R.id.content);
        shadow = findViewById(R.id.shadow);
        if (contentResId != 0) {
            contentInner.addView(inflate(getContext(), contentResId, null));
        }
        return this;
    }

    /**
     * Configure the instance of AnimationHelper
     * that provide the base animation
     * of the library
     */
    public void configSlideHelper() {
        if (animationHelper == null) {
            animationHelper = new AnimationHelper(getContext(), this);
        }
    }

    /**
     * Configure the size of the Toolbars based
     * on a height param
     */
    private void configSizes() {
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, toolbarHeight + innerPadding * 2);
        outToolbar.setLayoutParams(layoutParams);
        RelativeLayout.LayoutParams layoutParamsShadow = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, toolbarHeight);
        layoutParamsShadow.setMargins(innerPadding, innerPadding, innerPadding, innerPadding);
        shadowLayout.setLayoutParams(layoutParamsShadow);
    }

    /**
     * Initialize some attributes to provide
     * the height, padding and content to CultView
     */
    private void getStyle(Context context, AttributeSet attrs, int defStyleAttr) {
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.cult_view, defStyleAttr, 0);
            toolbarHeight = (int) a.getDimension(R.styleable.cult_view_toolbar_height, DEFAULT_HEIGHT);
            innerPadding = (int) a.getDimension(R.styleable.cult_view_inner_padding, DEFAULT_PADDING);
            contentResId = a.getResourceId(R.styleable.cult_view_content_view, 0);
            a.recycle();
        }
    }

    /**
     * This method is used to set
     * the padding to shadowLayout
     *
     * @param left left padding
     * @param top top padding
     * @param right right padding
     * @param bottom bottom padding
     * @return instance of CultView
     */
    public CultView setInnerPadding(int left, int top, int right, int bottom) {
        shadowLayout.setPadding(ViewUtil.dpToPx(getResources(), left), ViewUtil.dpToPx(getResources(), top),
                ViewUtil.dpToPx(getResources(), right), ViewUtil.dpToPx(getResources(), bottom));
        return this;
    }

    /**
     * Set the OutToolbar layout based of the @resourceId
     *
     * @param resourceId id of layout
     */
    public void setOutToolbarLayout(int resourceId) {
        outToolbar.addView(inflate(getContext(), resourceId, null));
    }

    /**
     * Set the OutToolbar layout based of the view instance
     *
     * @param view View of layout
     */
    public void setOutToolbarLayout(View view) {
        outToolbar.addView(view);
    }

    /**
     * Set the OutContent layout based of the @resourceId
     *
     * @param resourceId id of layout
     */
    public void setOutContentLayout(int resourceId) {
        contentOut.addView(inflate(getContext(), resourceId, null));
    }

    /**
     * Set the OutContent layout based of the view instance
     *
     * @param view View of layout
     */
    public void setOutContentLayout(View view) {
        contentOut.addView(view);
    }

    public void showFade() {
        showFade(0);
    }

    public void showFade(long duration) {
        if (verifyAnimationRunning()) {
            showShadow(duration);
            animationHelper.fadeIn(outToolbar, duration);
            showContentSlide(duration);
        }
    }

    public void showSlide() {
        showSlide(0);
    }

    public void showSlide(long duration) {
        if (verifyAnimationRunning()) {
            showShadow(duration);
            animationHelper.slideInTop(outToolbar, duration);
            showContentSlide(duration);
        }
    }

    public void hideSlideBottom() {
        hideSlideBottom(0);
    }

    public void hideSlideBottom(long duration) {
        if (verifyAnimationRunning()) {
            hideShadow(duration);
            animationHelper.slideOutBottom(outToolbar, duration);
            hideContentSlide(duration);
        }
    }

    public void hideSlideTop() {
        hideSlideTop(0);
    }

    public void hideSlideTop(long duration) {
        if (verifyAnimationRunning()) {
            hideShadow(duration);
            animationHelper.slideOutTop(outToolbar, duration);
            hideContentSlide(duration);
        }
    }

    public void hideFade() {
        hideFade(0);
    }

    public void hideFade(long duration) {
        if (verifyAnimationRunning()) {
            hideShadow(duration);
            animationHelper.fadeOut(outToolbar, duration);
            hideContentSlide(duration);
        }
    }

    public void showContentSlide(long duration) {
        animationHelper.slideInBottom(contentOut, duration);
    }

    public void hideContentSlide(long duration) {
        animationHelper.slideOutBottom(contentOut, duration);
    }

    public void hideShadow(long duration) {
        animationHelper.fadeOut(shadow, duration);
    }

    public void showShadow(long duration) {
        animationHelper.fadeIn(shadow, duration);
    }

    /**
     * Realize an smooth slide to an slide offset passed as argument. This method is the base of
     * maximize, minimize and close methods.
     *
     * @return true if the view is slided.
     */
    private boolean smoothSlideTo(View view, int x, int y) {
        if (dragHelper.smoothSlideViewTo(view, x, y)) {
            ViewCompat.postInvalidateOnAnimation(this);
            return true;
        }
        return false;
    }

    /**
     * @return if outToolbar and contentOut is visible
     */
    public boolean isSecondViewAdded() {
        return outToolbar.getVisibility() == VISIBLE && contentOut.getVisibility() == VISIBLE;
    }

    private void setVerticalDragRange(float verticalDragRange) {
        this.verticalDragRange = verticalDragRange;
    }

    public float getVerticalDragRange() {
        return verticalDragRange;
    }

    /**
     * This class verify if animation is running,
     * if it isn't running, set is running and
     * return true
     *
     * @return if animation is running
     */
    public boolean verifyAnimationRunning() {
        if (!isAnimationRunning) {
            isAnimationRunning = true;
            return true;
        } else {
            return false;
        }
    }

    public void setAnimationRunning(Boolean isAnimationRunning) {
        this.isAnimationRunning = isAnimationRunning;
    }

    public Toolbar getInnerToolbar() {
        return innerToolbar;
    }

    public Toolbar getOutToolbar() {
        return outToolbar;
    }

}