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