Android Open Source - budget-envelopes Delete View






From Project

Back to project page budget-envelopes.

License

The source code is released under:

GNU General Public License

If you think the Android project budget-envelopes 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

/*
 * This file is a part of Budget with Envelopes.
 * Copyright 2013 Michael Howell <michael@notriddle.com>
 *//from www  .java 2  s. co m
 * Budget is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Budget is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Budget. If not, see <http://www.gnu.org/licenses/>.
 */

package com.notriddle.budget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.view.animation.LinearInterpolator;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Checkable;
import android.widget.FrameLayout;
import java.util.ArrayList;

public class DeleteView extends FrameLayout implements Checkable {
    public static interface OnDeleteListener {
        public void onDelete(DeleteView v);
    };

    private static final int STATE_READY = 0;
    private static final int STATE_PRESSED = 1;
    private static final int STATE_IN_SWIPE = 2;
    private static final int STATE_ANIMATING = 3;
    private static final int STATE_DELETING = 4;

    View mInnerView;
    float mSwipeStart;
    long mSwipeStartTime;
    int mSwipeState;
    int mBackground;
    int mTouchSlop;
    int mFlingSlop;
    int mFlingCap;
    int mPressTimeout;
    Runnable mPressRunnable;
    int mLongPressTimeout;
    Runnable mLongPressRunnable;
    Runnable mUnpressRunnable;
    VelocityTracker mVelocityTracker;
    ObjectAnimator mAnim;
    boolean mChecked;
    OnDeleteListener mListener;

    public DeleteView(Context cntx) {
        super(cntx);
        init(cntx);
    }
    public DeleteView(Context cntx, AttributeSet attrs) {
        super(cntx, attrs);
        init(cntx);
    }
    public DeleteView(Context cntx, AttributeSet attrs, int defStyleAttr) {
        super(cntx, attrs, defStyleAttr);
        init(cntx);
    }
    private void init(Context cntx) {
        mInnerView = null;
        mSwipeStart = -1;
        mSwipeStartTime = -1;
        mBackground = 0;
        ViewConfiguration config = ViewConfiguration.get(cntx);
        mTouchSlop = config.getScaledTouchSlop();
        mFlingSlop = config.getScaledMinimumFlingVelocity();
        mFlingCap = config.getScaledMaximumFlingVelocity();
        mPressTimeout = config.getTapTimeout();
        mPressRunnable = new Runnable() {
            public void run() {
                if (mSwipeState == STATE_PRESSED) {
                    mInnerView.setPressed(true);
                }
            }
        };
        mLongPressTimeout = mPressTimeout + config.getLongPressTimeout();
        mLongPressRunnable = new Runnable() {
            public void run() {
                if (mSwipeState == STATE_PRESSED) {
                    performLongClick();
                    mInnerView.setPressed(false);
                }
            }
        };
        mUnpressRunnable = new Runnable() {
            public void run() {
                mInnerView.setPressed(false);
            }
        };
        mSwipeState = STATE_READY;
        mVelocityTracker = null;
        mAnim = null;
        mChecked = false;
        mListener = null;
        setClickable(true);
    }

    @Override public void onFinishInflate() {
        super.onFinishInflate();
        mInnerView = getChildAt(0);
    }

    @Override public boolean isChecked() {
        return mChecked;
    }
    @Override public void setChecked(boolean checked) {
        mChecked = checked;
        if (mInnerView instanceof Checkable) {
            ((Checkable)mInnerView).setChecked(checked);
        }
    }
    @Override public void toggle() {
        setChecked(!mChecked);
    }

    @Override public boolean onTouchEvent(MotionEvent event) {
        float x = event.getRawX();
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                switch (mSwipeState) {
                    case STATE_READY:
                        mSwipeState = STATE_PRESSED;
                        mSwipeStart = x;
                        mSwipeStartTime = event.getEventTime();
                        removeCallbacks(mUnpressRunnable);
                        postDelayed(mPressRunnable, mPressTimeout);
                        postDelayed(mLongPressRunnable, mLongPressTimeout);
                        break;
                    case STATE_ANIMATING:
                        cancelAnimation(x);
                        break;
                    case STATE_DELETING:
                        // Do nothing.
                        break;
                    default:
                        throw new Error("Invalid state with ACTION_DOWN: "+mSwipeState);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                switch (mSwipeState) {
                    case STATE_PRESSED:
                        if (Math.abs(x - mSwipeStart) > mTouchSlop) {
                            cancelPressed();
                            startSwipe();
                        }
                        break;
                    case STATE_IN_SWIPE:
                        setInnerViewPosition(event.getRawX() - mSwipeStart);
                        mVelocityTracker.addMovement(event);
                        break;
                    case STATE_ANIMATING:
                        cancelAnimation(x);
                        break;
                    case STATE_DELETING:
                        // Do nothing.
                        break;
                    default:
                        throw new Error("Invalid state with ACTION_MOVE: "+mSwipeState);
                }
                break;
            case MotionEvent.ACTION_UP:
                switch (mSwipeState) {
                    case STATE_PRESSED:
                        if (event.getEventTime() - mSwipeStartTime > mLongPressTimeout) {
                            // Long click is already performed.
                        } else {
                            mInnerView.setPressed(true);
                            performClick();
                        }
                        cancelPressed();
                        mSwipeState = STATE_READY;
                        break;
                    case STATE_IN_SWIPE:
                        startAnimation();
                        break;
                    case STATE_ANIMATING:
                    case STATE_DELETING:
                        // Do nothing.
                        break;
                    default:
                        throw new Error("Invalid state with ACTION_UP: "+mSwipeState);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                switch (mSwipeState) {
                    case STATE_PRESSED:
                        cancelPressed();
                        mSwipeState = STATE_READY;
                        break;
                    case STATE_IN_SWIPE:
                        startAnimation();
                        break;
                    case STATE_READY:
                    case STATE_ANIMATING:
                    case STATE_DELETING:
                        // Do nothing.
                        break;
                    default:
                        throw new Error("Invalid state with ACTION_CANCEL: "+mSwipeState);
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
                // We don't do anything with multitouch.
                break;
            default:
                throw new Error("Invalid MotionEvent: "+event.getActionMasked());
        }

        Log.d("Budget", "onTouchEvent(): "+mSwipeState);

        return true;
    }

    private void cancelPressed() {
        postDelayed(mUnpressRunnable, 100);
        removeCallbacks(mPressRunnable);
        removeCallbacks(mLongPressRunnable);
    }

    private void startSwipe() {
        if (getParent() instanceof ViewGroup) {
            ViewGroup p = (ViewGroup)getParent();
            p.requestDisallowInterceptTouchEvent(true);
        }
        mSwipeState = STATE_IN_SWIPE;
        setBackgroundResource(mBackground);
        mVelocityTracker = VelocityTracker.obtain();
        setClickable(false);
    }

    private void cancelSwipe() {
        if (getParent() instanceof ViewGroup) {
            ViewGroup p = (ViewGroup)getParent();
            p.requestDisallowInterceptTouchEvent(false);
        }
        mSwipeState = STATE_READY;
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
        if (mAnim != null) {
            ObjectAnimator anim = mAnim;
            mAnim = null;
            anim.cancel();
        }
        setClickable(true);
        setInnerViewPosition(0);
        setBackgroundResource(0);
        setInnerViewHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    public void performDelete() {
        Log.d("Budget", "performDelete()");
        if (getParent() instanceof ViewGroup) {
            ((ViewGroup)getParent()).requestDisallowInterceptTouchEvent(false);
        }
        mSwipeState = STATE_DELETING;
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
        setClickable(false);
        final int oldPadding = getPaddingY();
        final ObjectAnimator paddingAnim = ObjectAnimator.ofInt(
            this, "paddingY", oldPadding, 0
        );
        paddingAnim.setDuration(250);
        paddingAnim.start();
        mAnim = ObjectAnimator.ofInt(
            this, "innerViewHeight", mInnerView.getHeight(), 0
        );
        mAnim.setDuration(250);
        mAnim.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                if (mAnim != null) {
                    mListener.onDelete(DeleteView.this);
                    Log.d("Budget", "performDelete(): done");
                } else {
                    Log.d("Budget", "performDelete(): canceled");
                    paddingAnim.cancel();
                    setPaddingY(oldPadding);
                }
            }
        });
        mAnim.start();
    }

    private void startAnimation() {
        mVelocityTracker.computeCurrentVelocity(1);
        float velocityChk = mVelocityTracker.getXVelocity();
        final float position = mInnerView.getTranslationX();
        float velocityChk2 = velocityChk == 0 ? -position : velocityChk;
        if (Math.abs(velocityChk2) > mFlingCap) {
            float velocityChk2Sign = velocityChk2 / Math.abs(velocityChk2);
            velocityChk2 = velocityChk2Sign * mFlingCap;
        }
        final float velocity = velocityChk2;
        final float farPosition = (position > 0 ? 1 : -1) * mInnerView.getWidth();
        final float finalPosition = ((velocity / position) > 0)
                                    ? farPosition : 0;
        final float displacement = finalPosition - position;
        mAnim = ObjectAnimator.ofFloat(
            this, "innerViewPosition",
            position, finalPosition
        );
        mAnim.setDuration((long)Math.min(Math.abs(displacement / velocity), 500));
        mAnim.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                if (mAnim != null) {
                    if (finalPosition == 0) {
                        cancelSwipe();
                    } else {
                        performDelete();
                    }
                }
            }
        });
        mAnim.setInterpolator(new LinearInterpolator());
        mSwipeState = STATE_ANIMATING;
        mAnim.start();
    }

    private void cancelAnimation(float newStartPosition) {
        if (mAnim != null) {
            ObjectAnimator anim = mAnim;
            mAnim = null;
            anim.cancel();
        }
        mVelocityTracker.clear();
        mSwipeStart = newStartPosition - mInnerView.getTranslationX();
        mSwipeState = STATE_IN_SWIPE;
    }

    private void setInnerViewPosition(float position) {
        mInnerView.setTranslationX(position);
        float width = mInnerView.getWidth();
        mInnerView.setAlpha((width-Math.abs(position))/width);
        //Log.d("Budget", "setInnerViewPosition("+position+")");
    }
    private void setInnerViewHeight(int height) {
        mInnerView.getLayoutParams().height = height;
        mInnerView.requestLayout();
        //Log.d("Budget", "setInnerViewHeight("+height+")");
    }

    public void setInnerView(View innerView) {
        if (mInnerView != null) {
            removeView(innerView);
        }
        mInnerView = innerView;
        addView(innerView);
        if (mInnerView instanceof Checkable) {
            ((Checkable)mInnerView).setChecked(mChecked);
        }
        if (mSwipeState == STATE_PRESSED) {
            cancelPressed();
            mSwipeState = STATE_READY;
        } else if (mSwipeState != STATE_READY) {
            cancelSwipe();
        }
    }
    public View getInnerView() {
        return mInnerView;
    }

    public int getPaddingY() {
        return getPaddingTop();
    }
    public void setPaddingY(int pad) {
        setPadding(getPaddingLeft(), pad, getPaddingRight(), pad);
    }

    public void setSwipeBackgroundResource(int background) {
        mBackground = background;
    }

    public void setOnDeleteListener(OnDeleteListener listener) {
        mListener = listener;
    }
}




Java Source Code List

com.notriddle.budget.AboutFragment.java
com.notriddle.budget.AllTransactionsFragment.java
com.notriddle.budget.CheckableLinearLayout.java
com.notriddle.budget.CheckableRelativeLayout.java
com.notriddle.budget.ColorFragment.java
com.notriddle.budget.CustomActionBarFragment.java
com.notriddle.budget.DeleteAdapter.java
com.notriddle.budget.DeleteView.java
com.notriddle.budget.EditMoney.java
com.notriddle.budget.EditTextDefaultFocus.java
com.notriddle.budget.EditTransactionFragment.java
com.notriddle.budget.EnvelopeDetailsFragment.java
com.notriddle.budget.EnvelopesActivity.java
com.notriddle.budget.EnvelopesAdapter.java
com.notriddle.budget.EnvelopesFragment.java
com.notriddle.budget.EnvelopesOpenHelper.java
com.notriddle.budget.ExportFragment.java
com.notriddle.budget.FileCreatorFragment.java
com.notriddle.budget.GraphFragment.java
com.notriddle.budget.ImportFragment.java
com.notriddle.budget.LockedActivity.java
com.notriddle.budget.LogAdapter.java
com.notriddle.budget.MonitorScrollView.java
com.notriddle.budget.NavAdapter.java
com.notriddle.budget.OkFragment.java
com.notriddle.budget.PaycheckEnvelopesAdapter.java
com.notriddle.budget.PaycheckFragment.java
com.notriddle.budget.PinActivity.java
com.notriddle.budget.SQLiteLoader.java
com.notriddle.budget.SettingsFragment.java
com.notriddle.budget.SimpleEnvelopesAdapter.java
com.notriddle.budget.SimpleLogAdapter.java
com.notriddle.budget.SpendFragment.java
com.notriddle.budget.TACGridView.java
com.notriddle.budget.TitleFragment.java
com.notriddle.budget.TransferFragment.java
com.notriddle.budget.Util.java
com.notriddle.budget.WidgetProvider.java
com.notriddle.budget.WidgetService.java
com.notriddle.budget.csv.CSVReader.java
com.notriddle.budget.csv.CSVWriter.java