随机飞入效果视图控件

来源:互联网 发布:苹果整理桌面软件 编辑:程序博客网 时间:2024/06/07 13:16

效果图:这里写图片描述
随机飞入效果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;            }        }    }}
原创粉丝点击