随机飞入效果的实现

最近在看一些随机飞入的效果的东西,现在觉得网上的一些帖子有的写的不是很全,有的没有说明情景,用起来很费劲而且并不能很好的理解。最近整理了之前实现的这个效果,抽取了必要的代码,需要的可以直接使用。这篇博文主要对随机飞入效果实现的说明。

1. 效果图

这里写图片描述

有没有很酷?是的,效果确实非常的赞。因为这里用到的类比较多,而且也比较长,这里我先写下实现步骤,最后再附上抽取类的源代码。

2. 实现步骤

2.1 XML代码中引入

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.example.administrator.p2pinvest.ui.randomLayout.StellarMap
            android:id="@+id/stellarMap"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </com.example.administrator.p2pinvest.ui.randomLayout.StellarMap>

    </LinearLayout>

2.2 初始化布局,这里使用了ButterKnife注解的方式初始化控件

    @Bind(R.id.stellarMap)
    StellarMap stellarMap;

2.3 数据集合的创建

    private String[] datas = new String[]{"超级新手计划", "乐享活系列90天计划", "钱包计划", "30天理财计划(加息2%)", "90天理财计划(加息5%)", "180天理财计划(加息10%)",
            "林业局投资商业经营", "中学老师购买车辆", "屌丝下海经商计划", "新西游影视拍摄投资", "Java培训老师自己周转", "养猪场扩大经营",
            "旅游公司扩大规模", "阿里巴巴洗钱计划", "铁路局回款计划", "高级白领赢取白富美投资计划"
    };

    //分为两组数据
    private String[] firstDatas = new String[datas.length / 2];
    private String[] secondDatas = new String[datas.length - datas.length / 2];
    private Random random;

2.4 在onCreate中使用

    /**
     *@param content
     */
    @Override
    public void initData(String content) {
        //给两组数据赋值
        for (int i = 0; i < datas.length; i++) {
            if (i < datas.length / 2) {
                firstDatas[i] = datas[i];
            } else {
                secondDatas[i - datas.length / 2] = datas[i];
            }
        }
        StellarAdapter adapter = new StellarAdapter();
        //加载显示
        stellarMap.setAdapter(adapter);

        //必须提供如下两个方法的调用
        //设置初始化显示的组别,以及是否使用动画
        stellarMap.setGroup(0,true);

        //设置x,y轴方向上的稀疏度
        stellarMap.setRegularity(10,10);
    }

2.5 设置StellMapAdapter的适配器

    class StellarAdapter 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
         *
         * @param group
         * @param position    :对于每组数据来讲,position都是从0开始的
         * @param convertView
         * @return
         */
        @Override
        public View getView(int group, int position, View convertView) {

            random = new Random();
            final TextView textView = new TextView(getActivity());
            if (group == 0) {
                textView.setText(firstDatas[position]);
            } else {
                textView.setText(secondDatas[position]);
            }

            int red = random.nextInt(200);
            int green = random.nextInt(200);
            int blue = random.nextInt(200);
            textView.setTextColor(Color.rgb(red,green,blue));

            //设置字体大小
            textView.setTextSize(random.nextInt(UIUtils.dp2px(10))+UIUtils.dp2px(10));

            //给textView设置监听
            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    Log.e("TAG", "context =="+getContext());//MainActivity
                    Toast.makeText(getContext(), ""+textView.getText(), Toast.LENGTH_SHORT).show();
                }
            });
            return textView;
        }

        /**
         * 下一组显示平移动画的组别,查看源代码发现,此方法从未被调用
         * @param group
         * @param degree
         * @return
         */
        @Override
        public int getNextGroupOnPan(int group, float degree) {
            return 0;
        }

        /**
         * 下一组显示缩放的动画的组别
         * @param group
         * @param isZoomIn
         * @return
         */
        @Override
        public int getNextGroupOnZoom(int group, boolean isZoomIn) {
            if(group == 0) {
                return 1;
            }else {

                return 0;
            }
        }
    }

到此,随机飞入飞出的效果已经完成。

3. 抽取出的类的代码

3.1 飞入动画样式的类 AnimationUtil.class

    package com.example.administrator.p2pinvest.ui.randomLayout;

    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;
        }
    }

自定义控制view显示位置的类:RandomLayout.class

    package com.example.administrator.p2pinvest.ui.randomLayout;

    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);
            }
        }
    }

3.3 ShakeListener.class

    package com.example.administrator.p2pinvest.ui.randomLayout;

    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();
        }
    }

3.4 随机飞入的实现类 StellarMap.class

    package com.example.administrator.p2pinvest.ui.randomLayout;

    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);
        }
    }

如果有什么问题,欢迎大家留言指正。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值