使用stellarMap实现飞流效果

在xml文件中设置自定义StellarMap

<p2ppinves.com.p2p.ui.randomLayout.StellarMap
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/stellar_map"></p2ppinves.com.p2p.ui.randomLayout.StellarMap>

首先导入几个类:

第一个类

package p2ppinves.com.p2p.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;
    }
}

第二个类

package p2ppinves.com.p2p.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);
        }
    }
}

第三各类

package p2ppinves.com.p2p.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();
    }
}

第四个类

package p2ppinves.com.p2p.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);
    }
}

第二步:调用StellarMap类实现这是在Fragment中实现的

package p2ppinves.com.p2p.fragment.InvestBottomFragment;

import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.loopj.android.http.RequestParams;

import java.util.Random;

import butterknife.Bind;
import butterknife.ButterKnife;
import p2ppinves.com.p2p.R;
import p2ppinves.com.p2p.common.BaseFragment;
import p2ppinves.com.p2p.ui.randomLayout.StellarMap;
import p2ppinves.com.p2p.util.UIUtils;

/**
 * 如何在布局中加载显示子视图?
 * 1.静态的:直接在标签中添加
 * 2.动态的,
 * ----》addview(...)一个一个添加
 * ----》设置adapter的方式,批量的装配数据
 */

public class ProductRecommondFragment extends BaseFragment {

    @Bind(R.id.stellar_map)
    StellarMap stellarMap;
    //提供装配的数据
    private String[] datas = new String[]{"超级新手计划", "乐享活系列90天计划", "钱包计划", "30天理财计划(加息2%)", "90天理财计划(加息5%)", "180天理财计划(加息10%)",
            "林业局投资商业经营", "中学老师购买车辆", "屌丝下海经商计划", "新西游影视拍摄投资", "Java培训老师自己周转", "养猪场扩大经营",
            "旅游公司扩大规模", "阿里巴巴洗钱计划", "铁路局回款计划", "高级白领赢取白富美投资计划"
    };
    //声明两个组
    private String[] oneDatas=new String[datas.length/2];
    private String[] towDatas=new String[datas.length-datas.length/2];
    private Random random=new Random();
    @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 {
                towDatas[i-datas.length/2]=datas[i];
            }

        }
        StellarAdapter adapter=new StellarAdapter();
        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_produccommond;
    }

    //提供Adapter的实现类
    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
        //position:不同的组别都是重0开始的。所以需要拆开俩个组
        @Override
        public View getView(int group, int position, View convertView) {
            final TextView textView=new TextView(getActivity());
            //设置属性
            //设置文本内容
            if(group==0){
                textView.setText(oneDatas[position]);
            }else {
                textView.setText(towDatas[position]);
            }
            //设置字体的大小
            textView.setTextSize(UIUtils.dp2px(10)+UIUtils.dp2px(random.nextInt(5)));
            //设置字体的颜色
            int red=random.nextInt(211);//00-ff:0-255
            int green=random.nextInt(211);
            int blue=random.nextInt(211);
            textView.setTextColor(Color.rgb(red,green,blue));
            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    UIUtils.toast(textView.getText().toString());
                }
            });
            return textView;
        }
        //返回下一组显示平移动画的组别。查看源码发现此方法从未被调用
        @Override
        public int getNextGroupOnPan(int group, float degree) {
            return 0;
        }
        //返回下一组缩放动画的组别
        @Override
        public int getNextGroupOnZoom(int group, boolean isZoomIn) {
            if(group==0)
                return 1;

            return 0;
        }
    }
}

转载于:https://my.oschina.net/u/3486497/blog/903754

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值