im.ene.lab.design.widget.swipecards.TinderView.java Source code

Java tutorial

Introduction

Here is the source code for im.ene.lab.design.widget.swipecards.TinderView.java

Source

/*
 * Copyright (c) 2015 Eneim Labs. All rights reserved.
 *
 * 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 im.ene.lab.design.widget.swipecards;

import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.PointF;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Adapter;

import im.ene.lab.design.R;
import im.ene.lab.design.helper.Util;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by dionysis_lorentzos on 5/8/14
 * for package com.lorentzos.swipecards
 * and project Swipe cards.
 * Use with caution dinosaurs might appear!
 */
public class TinderView extends BaseFlingAdapterView {

    static final String WIDGET_PACKAGE_NAME = TinderView.class.getPackage().getName();
    static final ThreadLocal<Map<String, Constructor<TinderView.Style>>> sConstructors;
    static final Class<?>[] CONSTRUCTOR_PARAMS;

    static {
        CONSTRUCTOR_PARAMS = new Class[] { Context.class, AttributeSet.class };
        sConstructors = new ThreadLocal<>();
    }

    private final Style mStyle;
    private int MAX_VISIBLE = 4;
    private int MIN_ADAPTER_STACK = 6;
    private float ROTATION_DEGREES = 15.f;
    private int LAST_OBJECT_IN_STACK = 0;
    private Adapter mAdapter;
    private OnSwipeListener mFlingListener;
    private AdapterDataSetObserver mDataSetObserver;
    private boolean isInLayout = false;
    private View mTopView = null;
    private OnItemClickListener mOnItemClickListener;
    private OnTopViewTouchListener onTopViewTouchListener;
    private PointF mLastTouchPoint;

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

    public TinderView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.swipeStyle);
    }

    public TinderView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StackView, defStyle, 0);
        MAX_VISIBLE = a.getInt(R.styleable.StackView_max_visible_children, MAX_VISIBLE);
        MIN_ADAPTER_STACK = a.getInt(R.styleable.StackView_min_adapter_stack, MIN_ADAPTER_STACK);
        ROTATION_DEGREES = a.getFloat(R.styleable.StackView_rotation_degrees, ROTATION_DEGREES);
        String layoutStyle = a.getString(R.styleable.StackView_layout_style);
        a.recycle();

        Style style = parseStyle(context, attrs, layoutStyle);
        mStyle = style == null ? new SimpleStyle() : style;
    }

    // learn from CoordinatorLayout
    private static Style parseStyle(Context context, AttributeSet attrs, String name) {
        if (TextUtils.isEmpty(name)) {
            return null;
        } else {
            String fullName;
            if (name.startsWith(".")) {
                fullName = context.getPackageName() + name;
            } else if (name.indexOf(46) >= 0) {
                fullName = name;
            } else {
                fullName = WIDGET_PACKAGE_NAME + '.' + name;
            }

            try {
                Map e = (Map) sConstructors.get();
                if (e == null) {
                    e = new HashMap();
                    sConstructors.set(e);
                }

                Constructor c = (Constructor) e.get(fullName);
                if (c == null) {
                    Class clazz = Class.forName(fullName, true, context.getClassLoader());
                    c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
                    c.setAccessible(true);
                    e.put(fullName, c);
                }

                return (Style) c.newInstance(context, attrs);
            } catch (Exception var7) {
                throw new RuntimeException("Could not inflate Style subclass " + fullName, var7);
            }
        }
    }

    /**
     * A shortcut method to set both the listeners and the adapter.
     *
     * @param context  The activity context which extends onFlingListener, OnItemClickListener or
     *                 both
     * @param mAdapter The adapter you have to set.
     */
    public void init(final Context context, Adapter mAdapter) {
        if (context instanceof OnSwipeListener) {
            mFlingListener = (OnSwipeListener) context;
        } else {
            throw new RuntimeException("Activity does not implement SwipeFlingAdapterView" + ".onFlingListener");
        }
        if (context instanceof OnItemClickListener) {
            mOnItemClickListener = (OnItemClickListener) context;
        }
        setAdapter(mAdapter);
    }

    @Override
    public View getTopView() {
        return mTopView;
    }

    @Override
    public void requestLayout() {
        if (!isInLayout) {
            super.requestLayout();
        }
    }

    public OnTopViewTouchListener getTopCardListener() throws NullPointerException {
        if (onTopViewTouchListener == null) {
            throw new NullPointerException();
        }
        return onTopViewTouchListener;
    }

    public void setMaxVisibleChildren(int MAX_VISIBLE) {
        this.MAX_VISIBLE = MAX_VISIBLE;
    }

    public void setMinStackInAdapter(int MIN_ADAPTER_STACK) {
        this.MIN_ADAPTER_STACK = MIN_ADAPTER_STACK;
    }

    public void setFlingListener(OnSwipeListener onSwipeListener) {
        this.mFlingListener = onSwipeListener;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    @Override
    public Adapter getAdapter() {
        return mAdapter;
    }

    @Override
    public void setAdapter(Adapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
            mDataSetObserver = null;
        }

        mAdapter = adapter;

        if (mAdapter != null && mDataSetObserver == null) {
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
        }
    }

    public interface OnSwipeListener {

        void onTopExited(View view);

        void onExitToLeft(View view);

        void onExitToRight(View view);

        void onAdapterAboutToEmpty(int itemsInAdapter);

        void onFlingTopView(View view, float offset);
    }

    /**
     * A helper class which enable user to customize children's behavior.
     * <p/>
     * We focus on swiping top view behavior and the appearance of new children
     */
    public abstract static class Style {

        public Style() {
        }

        public Style(Context context, AttributeSet attrs) {
        }

        /**
         * Override this to change the child laying out style
         *
         * @param child    : child need to be layout
         * @param index    : child's index
         * @param maxIndex : current stack's max index
         */
        protected void layoutNewChild(View child, int index, int maxIndex) {
        }

        /**
         * Override this to change the top view swiping behavior
         *
         * @param offset   : swiping offset, absolute value (0 .. 1)
         * @param topIndex : stack's top view index
         */
        protected void flingTopChild(float offset, int topIndex, View otherView, int otherViewIndex) {
        }
    }

    public static class TinderStyle extends Style {

        public Context mContext;

        public TinderStyle() {
            throw new IllegalArgumentException("This Style require a Context instance");
        }

        public TinderStyle(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.mContext = context;
        }

        @Override
        protected void layoutNewChild(View child, int index, int maxIndex) {
            float scaleFactor = index < maxIndex ? 1 - 0.05f * index : 1 - 0.05f * (index - 1);
            ViewCompat.setScaleX(child, scaleFactor);
            ViewCompat.setScaleY(child, scaleFactor);

            if (index < maxIndex)
                ViewCompat.setTranslationY(child, -Util.dpToPx(mContext, 24) * index);
            else {
                ViewCompat.setTranslationY(child, 0);
            }
        }

        @Override
        protected void flingTopChild(float offset, int topIndex, View child, int childIndex) {
            float minScaleFactor = 1 - 0.05f * childIndex;
            float maxScaleFactor = 1 - 0.05f * (childIndex - 1);
            float scaleFactor = minScaleFactor * (1 - offset) + maxScaleFactor * offset;

            float fromTransY = childIndex < topIndex ? -Util.dpToPx(mContext, 24) * childIndex : 0.0f;
            float toTransY = -Util.dpToPx(mContext, 24) * (childIndex - 1);
            float transY = fromTransY * (1 - offset) + toTransY * offset;
            if (childIndex < topIndex) {
                ViewCompat.setScaleX(child, scaleFactor);
                ViewCompat.setScaleY(child, scaleFactor);
            }

            ViewCompat.setTranslationY(child, transY);
        }
    }

    /**
     * a Styling which just do nothing
     */
    public class SimpleStyle extends Style {

        public SimpleStyle() {
        }

        public SimpleStyle(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    }

    private class AdapterDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            requestLayout();
        }
    }
}