随机飞入效果视图控件

这篇博客介绍了一个包含AnimationUtil, RandomLayout, ShakeListener, StellarMap四个类的随机飞入效果视图控件。这个ui组件能实现元素动态飞入屏幕的动画效果,适合用于增加应用的交互趣味性。" 42007195,2892833,iOS:轻松关闭UITextField键盘的三种方法,"['iOS开发', 'uitextfield', '键盘交互']
摘要由CSDN通过智能技术生成

效果图:这里写图片描述
随机飞入效果ui视图控件文件夹randomLayout,文件中包含四个文件类
AnimationUtil.class、RandomLayout.class、ShakeListener.class、StellarMap.class四个文件类。
AnimationUtil.class


import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.ScaleAnimation;

public class AnimationUtil {

    private static final long MEDIUM = 500;

    /**
     * 创建一个淡入放大的动画
     */
    public static Animation createZoomInNearAnim() {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡入的动画
        anim = new AlphaAnimation(0f, 1f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new LinearInterpolator());
        ret.addAnimation(anim);
        // 创建一个放大的动画
        anim = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        return ret;
    }

    /**
     * 创建一个淡出放大的动画
     */
    public static Animation createZoomInAwayAnim() {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡出的动画
        anim = new AlphaAnimation(1f, 0f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        // 创建一个放大的动画
        anim = new ScaleAnimation(1, 3, 1, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        return ret;
    }

    /**
     * 创建一个淡入缩小的动画
     */
    public static Animation createZoomOutNearAnim() {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡入的动画
        anim = new AlphaAnimation(0f, 1f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new LinearInterpolator());
        ret.addAnimation(anim);
        // 创建一个缩小的动画
        anim = new ScaleAnimation(3, 1, 3, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        return ret;
    }

    /**
     * 创建一个淡出缩小的动画
     */
    public static Animation createZoomOutAwayAnim() {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡出的动画
        anim = new AlphaAnimation(1f, 0f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        // 创建一个缩小的动画
        anim = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        return ret;
    }

    /**
     * 创建一个淡入放大的动画
     */
    public static Animation createPanInAnim(float degree) {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡入动画
        anim = new AlphaAnimation(0f, 1f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new LinearInterpolator());
        ret.addAnimation(anim);
        // 创建一个放大动画
        final float pivotX = (float) (1 - Math.cos(degree)) / 2;
        final float pivotY = (float) (1 + Math.sin(degree)) / 2;

        anim = new ScaleAnimation(0.8f, 1, 0.8f, 1, Animation.RELATIVE_TO_SELF, pivotX, Animation.RELATIVE_TO_SELF,
                pivotY);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);

        return ret;
    }

    /**
     * 创建一个淡出缩小的动画
     */
    public static Animation createPanOutAnim(float degree) {
        AnimationSet ret;
        Animation anim;
        ret = new AnimationSet(false);
        // 创建一个淡出动画
        anim = new AlphaAnimation(1f, 0f);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);
        // 创建一个缩小动画
        final float pivotX = (float) (1 + Math.cos(degree)) / 2;
        final float pivotY = (float) (1 - Math.sin(degree)) / 2;
        anim = new ScaleAnimation(1, 0.8f, 1, 0.8f, Animation.RELATIVE_TO_SELF, pivotX, Animation.RELATIVE_TO_SELF,
                pivotY);
        anim.setDuration(MEDIUM);
        anim.setInterpolator(new DecelerateInterpolator());
        ret.addAnimation(anim);

        return ret;
    }
}

RandomLayout.class


import android.content.Context;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class RandomLayout extends ViewGroup {

    private Random mRdm;
    /**
     * X分布规则性,该值越高,子view在x方向的分布越规则、平均。最小值为1。
     */
    private int mXRegularity;
    /**
     * Y分布规则性,该值越高,子view在y方向的分布越规则、平均。最小值为1。
     */
    private int mYRegularity;
    /**
     * 区域个数
     */
    private int mAreaCount;
    /**
     * 区域的二维数组
     */
    private int[][] mAreaDensity;
    /**
     * 存放已经确定位置的View
     */
    private Set<View> mFixedViews;
    /**
     * 提供子View的adapter
     */
    private Adapter mAdapter;
    /**
     * 记录被回收的View,以便重复利用
     */
    private List<View> mRecycledViews;
    /**
     * 是否已经layout
     */
    private boolean mLayouted;
    /**
     * 计算重叠时候的间距
     */
    private int mOverlapAdd = 2;

    /**
     * 构造方法
     */
    public RandomLayout(Context context) {
        super(context);
        init();
    }

    /**
     * 初始化方法
     */
    private void init() {
        mLayouted = false;
        mRdm = new Random();
        setRegularity(1, 1);
        mFixedViews = new HashSet<View>();
        mRecycledViews = new LinkedList<View>();
    }

    public boolean hasLayouted() {
        return mLayouted;
    }

    /**
     * 设置mXRegularity和mXRegularity,确定区域的个数
     */
    public void setRegularity(int xRegularity, int yRegularity) {
        if (xRegularity > 1) {
            this.mXRegularity = xRegularity;
        } else {
            this.mXRegularity = 1;
        }
        if (yRegularity > 1) {
            this.mYRegularity = yRegularity;
        } else {
            this.mYRegularity = 1;
        }
        this.mAreaCount = mXRegularity * mYRegularity;//个数等于x方向的个数*y方向的个数
        this.mAreaDensity = new int[mYRegularity][mXRegularity];//存放区域的二维数组
    }

    /**
     * 设置数据源
     */
    public void setAdapter(Adapter adapter) {
        this.mAdapter = adapter;
    }

    /**
     * 重新设置区域,把所有的区域记录都归0
     */
    private void resetAllAreas() {
        mFixedViews.clear();
        for (int i = 0; i < mYRegularity; i++) {
            for (int j = 0; j < mXRegularity; j++) {
                mAreaDensity[i][j] = 0;
            }
        }
    }

    /**
     * 把复用的View加入集合,新加入的放入集合第一个。
     */
    private void pushRecycler(View scrapView) {
        if (null != scrapView) {
            mRecycledViews.add(0, scrapView);
        }
    }

    /**
     * 取出复用的View,从集合的第一个位置取出
     */
    private View popRecycler() {
        final int size = mRecycledViews.size();
        if (size > 0) {
            return mRecycledViews.remove(0);
        } else {
            return null;
        }
    }

    /**
     * 产生子View,这个就是listView复用的简化版,但是原理一样
     */
    private void generateChildren() {
        if (null == mAdapter) {
            return;
        }
        // 先把子View全部存入集合
        final int childCount = super.getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            pushRecycler(super.getChildAt(i));
        }
        // 删除所有子View
        super.removeAllViewsInLayout();
        // 得到Adapter中的数据量
        final int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            //从集合中取出之前存入的子View
            View convertView = popRecycler();
            //把该子View作为adapter的getView的历史View传入,得到返回的View
            View newChild = mAdapter.getView(i, convertView);
            if (newChild != convertView) {//如果发生了复用,那么newChild应该等于convertView
                // 这说明没发生复用,所以重新把这个没用到的子View存入集合中
                pushRecycler(convertView);
            }
            //调用父类的方法把子View添加进来
            super.addView(newChild, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        }
    }

    /**
     * 重新分配区域
     */
    public void redistribute() {
        resetAllAreas();//重新设置区域
        requestLayout();
    }

    /**
     * 重新更新子View
     */
    public void refresh() {
        resetAllAreas();//重新分配区域
        generateChildren();//重新产生子View
        requestLayout();
    }

    /**
     * 重写父类的removeAllViews
     */
    @Override
    public void removeAllViews() {
        super.removeAllViews();//先删除所有View
        resetAllAreas();//重新设置所有区域
    }

    /**
     * 确定子View的位置,这个就是区域分布的关键
     */
    @Override
    public void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        // 确定自身的宽高
        int thisW = r - l - this.getPaddingLeft() - this.getPaddingRight();
        int thisH = b - t - this.getPaddingTop() - this.getPaddingBottom();
        // 自身内容区域的右边和下边
        int contentRight = r - getPaddingRight();
        int contentBottom = b - getPaddingBottom();
        // 按照顺序存放把区域存放到集合中
        List<Integer> availAreas = new ArrayList<Integer>(mAreaCount);
        for (int i = 0; i < mAreaCount; i++) {
            availAreas.add(i);
        }

        int areaCapacity = (count + 1) / mAreaCount + 1;  //区域密度,表示一个区域内可以放几个View,+1表示至少要放一个
        int availAreaCount = mAreaCount; //可用的区域个数

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.GONE) { // gone掉的view是不参与布局
                continue;
            }

            if (!mFixedViews.contains(child)) {//mFixedViews用于存放已经确定好位置的View,存到了就没必要再次存放
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                // 先测量子View的大小
                int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.AT_MOST);//为子View准备测量的参数
                int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), MeasureSpec.AT_MOST);
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                // 子View测量之后的宽和高
                int childW = child.getMeasuredWidth();
                int childH = child.getMeasuredHeight();
                // 用自身的高度去除以分配值,可以算出每一个区域的宽和高
                float colW = thisW / (float) mXRegularity;
                float rowH = thisH / (float) mYRegularity;

                while (availAreaCount > 0) { //如果使用区域大于0,就可以为子View尝试分配
                    int arrayIdx = mRdm.nextInt(availAreaCount);//随机一个list中的位置
                    int areaIdx = availAreas.get(arrayIdx);//再根据list中的位置获取一个区域编号
                    int col = areaIdx % mXRegularity;//计算出在二维数组中的位置
                    int row = areaIdx / mXRegularity;
                    if (mAreaDensity[row][col] < areaCapacity) {// 区域密度未超过限定,将view置入该区域
                        int xOffset = (int) colW - childW; //区域宽度 和 子View的宽度差值,差值可以用来做区域内的位置随机
                        if (xOffset <= 0) {
                            xOffset = 1;
                        }
                        int yOffset = (int) rowH - childH;
                        if (yOffset <= 0) {
                            yOffset = 1;
                        }
                        // 确定左边,等于区域宽度*左边的区域
                        params.mLeft = getPaddingLeft() + (int) (colW * col + mRdm.nextInt(xOffset));
                        int rightEdge = contentRight - childW;
                        if (params.mLeft > rightEdge) {//加上子View的宽度后不能超出右边界
                            params.mLeft = rightEdge;
                        }
                        params.mRight = params.mLeft + childW;

                        params.mTop = getPaddingTop() + (int) (rowH * row + mRdm.nextInt(yOffset));
                        int bottomEdge = contentBottom - childH;
                        if (params.mTop > bottomEdge) {//加上子View的宽度后不能超出右边界
                            params.mTop = bottomEdge;
                        }
                        params.mBottom = params.mTop + childH;

                        if (!isOverlap(params)) {//判断是否和别的View重叠了
                            mAreaDensity[row][col]++;//没有重叠,把该区域的密度加1
                            child.layout(params.mLeft, params.mTop, params.mRight, params.mBottom);//布局子View
                            mFixedViews.add(child);//添加到已经布局的集合中
                            break;
                        } else {//如果重叠了,把该区域移除,
                            availAreas.remove(arrayIdx);
                            availAreaCount--;
                        }
                    } else {// 区域密度超过限定,将该区域从可选区域中移除
                        availAreas.remove(arrayIdx);
                        availAreaCount--;
                    }
                }
            }
        }
        mLayouted = true;
    }

    /**
     * 计算两个View是否重叠,如果重叠,那么他们之间一定有一个矩形区域是共有的
     */
    private boolean isOverlap(LayoutParams params) {
        int l = params.mLeft - mOverlapAdd;
        int t = params.mTop - mOverlapAdd;
        int r = params.mRight + mOverlapAdd;
        int b = params.mBottom + mOverlapAdd;

        Rect rect = new Rect();

        for (View v : mFixedViews) {
            int vl = v.getLeft() - mOverlapAdd;
            int vt = v.getTop() - mOverlapAdd;
            int vr = v.getRight() + mOverlapAdd;
            int vb = v.getBottom() + mOverlapAdd;
            rect.left = Math.max(l, vl);
            rect.top = Math.max(t, vt);
            rect.right = Math.min(r, vr);
            rect.bottom = Math.min(b, vb);
            if (rect.right >= rect.left && rect.bottom >= rect.top) {
                return true;
            }
        }
        return false;
    }

    /**
     * 内部类、接口
     */
    public static interface Adapter {

        public abstract int getCount();

        public abstract View getView(int position, View convertView);
    }

    public static class LayoutParams extends ViewGroup.LayoutParams {

        private int mLeft;
        private int mRight;
        private int mTop;
        private int mBottom;

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(int w, int h) {
            super(w, h);
        }
    }
}

ShakeListener.class


import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class ShakeListener implements SensorEventListener {
    private static final int FORCE_THRESHOLD = 250;
    private static final int TIME_THRESHOLD = 100;
    private static final int SHAKE_TIMEOUT = 500;
    private static final int SHAKE_DURATION = 1000;
    private static final int SHAKE_COUNT = 2;

    private SensorManager mSensorMgr;
    private float mLastX = -1.0f, mLastY = -1.0f, mLastZ = -1.0f;
    private long mLastTime;
    private OnShakeListener mShakeListener;
    private Context mContext;
    private int mShakeCount = 0;
    private long mLastShake;
    private long mLastForce;

    public ShakeListener(Context context) {
        mContext = context;
        resume();
    }

    public void setOnShakeListener(OnShakeListener listener) {
        mShakeListener = listener;
    }

    /**
     * 界面可见时候才监听摇晃
     */
    public void resume() {
        mSensorMgr = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        if (mSensorMgr == null) {
            throw new UnsupportedOperationException("Sensors not supported");
        }

        boolean supported = mSensorMgr.registerListener(this, mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);
        if (!supported) {
            mSensorMgr.unregisterListener(this);
            return;
        }
    }

    /**
     * 界面不可见时,需要关闭监听
     */
    public void pause() {
        if (mSensorMgr != null) {
            mSensorMgr.unregisterListener(this);
            mSensorMgr = null;
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        System.out.println("accuracy:" + accuracy);
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        System.out.println("x:" + event.values[SensorManager.DATA_X] + "  y:" + event.values[SensorManager.DATA_Y] + "  z:" + event.values[SensorManager.DATA_Z]);

        if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
            return;
        }

        long now = System.currentTimeMillis();

        if ((now - mLastForce) > SHAKE_TIMEOUT) {
            mShakeCount = 0;
        }

        if ((now - mLastTime) > TIME_THRESHOLD) {
            long diff = now - mLastTime;
            // 把X,Y,Z方向的距离除以时间,得出速度
            float speed = Math.abs(event.values[SensorManager.DATA_X] + event.values[SensorManager.DATA_Y] + event.values[SensorManager.DATA_Z] - mLastX - mLastY - mLastZ) / diff * 10000;
            if (speed > FORCE_THRESHOLD) {//如果速度大于某个值
                // 先把摇晃的次数+1,再判断是否超过了要换的次数,并且间隙大于特定的值
                if ((++mShakeCount >= SHAKE_COUNT) && (now - mLastShake > SHAKE_DURATION)) {
                    mLastShake = now;
                    mShakeCount = 0;
                    if (mShakeListener != null) {//回调我们的listener
                        mShakeListener.onShake();
                    }
                }
                mLastForce = now;
            }
            mLastTime = now;
            mLastX = event.values[SensorManager.DATA_X];
            mLastY = event.values[SensorManager.DATA_Y];
            mLastZ = event.values[SensorManager.DATA_Z];
        }
    }

    public interface OnShakeListener {
        public void onShake();
    }
}

StellarMap.class


import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.widget.FrameLayout;

public class StellarMap extends FrameLayout implements AnimationListener, OnTouchListener, OnGestureListener {

    private RandomLayout mHidenGroup;

    private RandomLayout mShownGroup;

    private Adapter mAdapter;
    private RandomLayout.Adapter mShownGroupAdapter;
    private RandomLayout.Adapter mHidenGroupAdapter;

    private int mShownGroupIndex;// 显示的组
    private int mHidenGroupIndex;// 隐藏的组
    private int mGroupCount;// 组数

    /**
     * 动画
     */
    private Animation mZoomInNearAnim;
    private Animation mZoomInAwayAnim;
    private Animation mZoomOutNearAnim;
    private Animation mZoomOutAwayAnim;

    private Animation mPanInAnim;
    private Animation mPanOutAnim;
    /**
     * 手势识别器
     */
    private GestureDetector mGestureDetector;

    /**
     * 构造方法
     */
    public StellarMap(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

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

    public StellarMap(Context context) {
        super(context);
        init();
    }

    /**
     * 初始化方法
     */
    private void init() {
        mGroupCount = 0;
        mHidenGroupIndex = -1;
        mShownGroupIndex = -1;
        mHidenGroup = new RandomLayout(getContext());
        mShownGroup = new RandomLayout(getContext());

        addView(mHidenGroup, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        mHidenGroup.setVisibility(View.GONE);
        addView(mShownGroup, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

        mGestureDetector = new GestureDetector(this);
        setOnTouchListener(this);
        //设置动画
        mZoomInNearAnim = AnimationUtil.createZoomInNearAnim();
        mZoomInNearAnim.setAnimationListener(this);
        mZoomInAwayAnim = AnimationUtil.createZoomInAwayAnim();
        mZoomInAwayAnim.setAnimationListener(this);
        mZoomOutNearAnim = AnimationUtil.createZoomOutNearAnim();
        mZoomOutNearAnim.setAnimationListener(this);
        mZoomOutAwayAnim = AnimationUtil.createZoomOutAwayAnim();
        mZoomOutAwayAnim.setAnimationListener(this);
    }

    /**
     * 设置隐藏组和显示组的x和y的规则
     */
    public void setRegularity(int xRegularity, int yRegularity) {
        mHidenGroup.setRegularity(xRegularity, yRegularity);
        mShownGroup.setRegularity(xRegularity, yRegularity);
    }

    private void setChildAdapter() {
        if (null == mAdapter) {
            return;
        }
        mHidenGroupAdapter = new RandomLayout.Adapter() {
            //取出本Adapter的View对象给HidenGroup的Adapter
            @Override
            public View getView(int position, View convertView) {
                return mAdapter.getView(mHidenGroupIndex, position, convertView);
            }

            @Override
            public int getCount() {
                return mAdapter.getCount(mHidenGroupIndex);
            }
        };
        mHidenGroup.setAdapter(mHidenGroupAdapter);

        mShownGroupAdapter = new RandomLayout.Adapter() {
            //取出本Adapter的View对象给ShownGroup的Adapter
            @Override
            public View getView(int position, View convertView) {
                return mAdapter.getView(mShownGroupIndex, position, convertView);
            }

            @Override
            public int getCount() {
                return mAdapter.getCount(mShownGroupIndex);
            }
        };
        mShownGroup.setAdapter(mShownGroupAdapter);
    }

    /**
     * 设置本Adapter
     */
    public void setAdapter(Adapter adapter) {
        mAdapter = adapter;
        mGroupCount = mAdapter.getGroupCount();
        if (mGroupCount > 0) {
            mShownGroupIndex = 0;
        }
        setChildAdapter();
    }

    /**
     * 设置显示区域
     */
    public void setInnerPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
        mHidenGroup.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
        mShownGroup.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
    }

    /**
     * 给指定的Group设置动画
     */
    public void setGroup(int groupIndex, boolean playAnimation) {
        switchGroup(groupIndex, playAnimation, mZoomInNearAnim, mZoomInAwayAnim);
    }

    /**
     * 获取当前显示的group角标
     */
    public int getCurrentGroup() {
        return mShownGroupIndex;
    }

    /**
     * 给Group设置动画入
     */
    public void zoomIn() {
        final int nextGroupIndex = mAdapter.getNextGroupOnZoom(mShownGroupIndex, true);
        switchGroup(nextGroupIndex, true, mZoomInNearAnim, mZoomInAwayAnim);
    }

    /**
     * 给Group设置出动画
     */
    public void zoomOut() {
        final int nextGroupIndex = mAdapter.getNextGroupOnZoom(mShownGroupIndex, false);
        switchGroup(nextGroupIndex, true, mZoomOutNearAnim, mZoomOutAwayAnim);
    }

    /**
     * 给Group设置动画
     */
    public void pan(float degree) {
        final int nextGroupIndex = mAdapter.getNextGroupOnPan(mShownGroupIndex, degree);
        mPanInAnim = AnimationUtil.createPanInAnim(degree);
        mPanInAnim.setAnimationListener(this);
        mPanOutAnim = AnimationUtil.createPanOutAnim(degree);
        mPanOutAnim.setAnimationListener(this);
        switchGroup(nextGroupIndex, true, mPanInAnim, mPanOutAnim);
    }

    /**
     * 给下一个Group设置进出动画
     */
    private void switchGroup(int newGroupIndex, boolean playAnimation, Animation inAnim, Animation outAnim) {
        if (newGroupIndex < 0 || newGroupIndex >= mGroupCount) {
            return;
        }
        //把当前显示Group角标设置为隐藏的
        mHidenGroupIndex = mShownGroupIndex;
        //把下一个Group角标设置为显示的
        mShownGroupIndex = newGroupIndex;
        // 交换两个Group
        RandomLayout temp = mShownGroup;
        mShownGroup = mHidenGroup;
        mShownGroup.setAdapter(mShownGroupAdapter);
        mHidenGroup = temp;
        mHidenGroup.setAdapter(mHidenGroupAdapter);
        //刷新显示的Group
        mShownGroup.refresh();
        //显示Group
        mShownGroup.setVisibility(View.VISIBLE);

        //启动动画
        if (playAnimation) {
            if (mShownGroup.hasLayouted()) {
                mShownGroup.startAnimation(inAnim);
            }
            mHidenGroup.startAnimation(outAnim);
        } else {
            mHidenGroup.setVisibility(View.GONE);
        }
    }

    // 重新分配显示区域
    public void redistribute() {
        mShownGroup.redistribute();
    }

    /**
     * 动画监听
     */
    @Override
    public void onAnimationStart(Animation animation) {
        // 当动画启动
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // 当动画结束
        if (animation == mZoomInAwayAnim || animation == mZoomOutAwayAnim || animation == mPanOutAnim) {
            mHidenGroup.setVisibility(View.GONE);
        }
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        // 当动画重复
    }

    /**
     * 定位
     */
    @Override
    public void onLayout(boolean changed, int l, int t, int r, int b) {
        //用以判断ShownGroup是否onLayout的变量
        boolean hasLayoutedBefore = mShownGroup.hasLayouted();
        super.onLayout(changed, l, t, r, b);
        if (!hasLayoutedBefore && mShownGroup.hasLayouted()) {
            mShownGroup.startAnimation(mZoomInNearAnim);//第一次layout的时候启动动画
        } else {
            mShownGroup.setVisibility(View.VISIBLE);
        }
    }

    /**
     * 重写onTouch事件,把onTouch事件分配给手势识别
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }

    /**
     * 消费掉onDown事件
     */
    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    /**
     * 空实现
     */
    @Override
    public void onShowPress(MotionEvent e) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        int centerX = getMeasuredWidth() / 2;
        int centerY = getMeasuredWidth() / 2;

        int x1 = (int) e1.getX() - centerX;
        int y1 = (int) e1.getY() - centerY;
        int x2 = (int) e2.getX() - centerX;
        int y2 = (int) e2.getY() - centerY;

        if ((x1 * x1 + y1 * y1) > (x2 * x2 + y2 * y2)) {
            zoomOut();
        } else {
            zoomIn();
        }
        return true;
    }

    /**
     * 内部类、接口
     */
    public static interface Adapter {
        public abstract int getGroupCount();

        public abstract int getCount(int group);

        public abstract View getView(int group, int position, View convertView);

        public abstract int getNextGroupOnPan(int group, float degree);

        public abstract int getNextGroupOnZoom(int group, boolean isZoomIn);
    }
}

使用示例:


import android.graphics.Color;
import android.view.View;
import android.widget.TextView;

import com.loopj.android.http.RequestParams;
import com.willkong.p2pclient.R;
import com.willkong.p2pclient.common.BaseFragment;
import com.willkong.p2pclient.ui.randomLayout.StellarMap;
import com.willkong.p2pclient.util.UIUtils;

import java.util.Random;

import butterknife.Bind;

/**
 * 作者: willkong on 2017/10/25.
 * 作用:推荐理财
 */
public class ProductRecommondFragment extends BaseFragment {
    private String[] datas = new String[]{"新手福利计划", "财神道90天计划", "硅谷钱包计划", "30天理财计划(加息2%)", "180天理财计划(加息5%)", "月月升理财计划(加息10%)",
            "中情局投资商业经营", "大学老师购买车辆", "屌丝下海经商计划", "美人鱼影视拍摄投资", "Android培训老师自己周转", "养猪场扩大经营",
            "旅游公司扩大规模", "摩托罗拉洗钱计划", "铁路局回款计划", "屌丝迎娶白富美计划"
    };
    //声明两个子数组
    private String[] oneDatas = new String[datas.length/2];
    private String[] twoDatas = new String[datas.length - datas.length/2];
    private Random random = new Random();
    @Bind(R.id.stellar_map)
    StellarMap stellarMap;

    @Override
    protected RequestParams getParams() {
        return null;
    }

    @Override
    protected String getUrl() {
        return null;
    }

    @Override
    protected void initData(String content) {
        //初始化子数组的数据
        for (int i = 0; i < datas.length; i++) {
            if (i<datas.length/2){
                oneDatas[i] = datas[i];
            }else {
                twoDatas[i - datas.length/2] = datas[i];
            }
        }
        StellarMapAdapter adapter = new StellarMapAdapter();
        stellarMap.setAdapter(adapter);
        int leftPadding = UIUtils.dp2px(10);
        int topPadding = UIUtils.dp2px(10);
        int rightPadding = UIUtils.dp2px(10);
        int bottomPadding = UIUtils.dp2px(10);
        stellarMap.setInnerPadding(leftPadding,topPadding,rightPadding,bottomPadding);
        //必须调用如下的两个方法,否则stellarMap不能显示数据
        //设置显示的数据在x轴,y轴方向上的稀疏度
        stellarMap.setRegularity(5,7);
        //设置初始化显示的组别,以及是否需要使用动画
        stellarMap.setGroup(0,true);
    }

    @Override
    protected void initTitle() {

    }

    @Override
    public int getLayoutId() {
        return R.layout.fragment_product_recommond;
    }

    //提供Adapter的实现类
    class StellarMapAdapter implements StellarMap.Adapter{

        //获取组的个数
        @Override
        public int getGroupCount() {
            return 2;
        }

        //返回每组中显示的数据的个数
        @Override
        public int getCount(int group) {
            if (group==0){
                return datas.length/2;
            }else {
                return datas.length - datas.length/2;
            }
        }

        //返回具体的View
        @Override
        public View getView(int group, int position, View convertView) {
            final TextView tv = new TextView(getActivity());
            //设置属性
            //设置文本的内容
            if (group==0){
                tv.setText(oneDatas[position]);
            }else {
                tv.setText(twoDatas[position]);
            }
            //设置字体的大小
            tv.setTextSize(UIUtils.dp2px(5)+UIUtils.dp2px(random.nextInt(5)));
            //设置字体的颜色
            int red = random.nextInt(211);
            int green = random.nextInt(211);
            int blue = random.nextInt(211);
            tv.setTextColor(Color.rgb(red,green,blue));
            //设置TextView的点击事件
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    UIUtils.toast(tv.getText().toString(),false);
                }
            });
            return tv;
        }

        //返回下一组显示平移动画的组别。
        @Override
        public int getNextGroupOnPan(int group, float degree) {
            return 0;
        }
        //返回下一组显示缩放动画的组别。
        @Override
        public int getNextGroupOnZoom(int group, boolean isZoomIn) {
            if (group==0){
                return 1;
            }else {
                return 0;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值