package cn.coderss.ui; import java.util.List; import cn.coderss.edu.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * android标签云 by loader * @author 亓斌 (qibin0506@gmail.com) * <p> * * 使用方法:<p> 在xml中配置:<p> <org.loader.labelview.LabelView<p> xmlns:label="http://schemas.android.com/apk/res/org.loader.labelview"<p> android:layout_marginTop="20dp"<p> android:id="@+id/lv"<p> android:layout_below="@id/et_input"<p> android:layout_width="wrap_content"<p> android:layout_height="wrap_content"<p> label:is_static="false"<p> android:background="@android:color/white"/><p> <p> setLabels设置标签,<p> setColorSchema设置配色方案, <p> setSpeeds设置每一个的x/y速度,<p> setOnItemClick可以监听item的click事件<p> 使用时,别忘了copy values目录下的attrs.xml到你的项目中<p> 可参考demo中的MainActivity.java文件<p> */ public class LabelView extends View { private static final int DIRECTION_LEFT = 0; // 向左 private static final int DIRECTION_RIGHT = 1; // 向右 private static final int DIRECITON_TOP = 2; // 向上 private static final int DIRECTION_BOTTOM = 3; // 向下 private boolean isStatic; // 是否静止, 默认false, 可用干xml : label:is_static="false" private int[][] mLocations; // 每个label的位置 x/y private int[][] mDirections; // 每个label的方向 x/y private int[][] mSpeeds; // 每个label的x/y速度 x/y private int[][] mTextWidthAndHeight; // 每个labeltext的大小 width/height private String[] mLabels; // 设置的labels private int[] mFontSizes; // 每个label的字体大小 // 默认配色方案 private int[] mColorSchema = {0XFFFF0000, 0XFF00FF00, 0XFF0000FF, 0XFFCCCCCC, 0XFFFFFFFF}; private int mTouchSlop; // 最小touch private int mDownX = -1; private int mDownY = -1; private int mDownIndex = -1; // 点击的index private Paint mPaint; private Thread mThread; private OnItemClickListener mListener; // item点击事件 public LabelView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LabelView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LabelView, defStyleAttr, 0); isStatic = ta.getBoolean(R.styleable.LabelView_is_static, false); ta.recycle(); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mPaint = new Paint(); mPaint.setAntiAlias(true); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); init(); } @Override protected void onDraw(Canvas canvas) { if(!hasContents()) { return; } for (int i = 0; i < mLabels.length; i++) { mPaint.setTextSize(mFontSizes[i]); if(i < mColorSchema.length) mPaint.setColor(mColorSchema[i]); else mPaint.setColor(mColorSchema[i-mColorSchema.length]); canvas.drawText(mLabels[i], mLocations[i][0], mLocations[i][1], mPaint); } } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = (int) ev.getX(); mDownY = (int) ev.getY(); mDownIndex = getClickIndex(); break; case MotionEvent.ACTION_UP: int nowX = (int) ev.getX(); int nowY = (int) ev.getY(); if (nowX - mDownX < mTouchSlop && nowY - mDownY < mTouchSlop && mDownIndex != -1 && mListener != null) { mListener.onItemClick(mDownIndex, mLabels[mDownIndex]); } mDownX = mDownY = mDownIndex = -1; break; } return true; } /** * 获取当前点击的label的位置 * @return label的位置,没有点中返回-1 */ private int getClickIndex() { Rect downRect = new Rect(); Rect locationRect = new Rect(); for(int i=0;i<mLocations.length;i++) { downRect.set(mDownX - mTextWidthAndHeight[i][0], mDownY - mTextWidthAndHeight[i][1], mDownX + mTextWidthAndHeight[i][0], mDownY + mTextWidthAndHeight[i][1]); locationRect.set(mLocations[i][0], mLocations[i][1], mLocations[i][0] + mTextWidthAndHeight[i][0], mLocations[i][1] + mTextWidthAndHeight[i][1]); if(locationRect.intersect(downRect)) { return i; } } return -1; } /** * 开启子线程不断刷新位置并postInvalidate */ private void run() { if(mThread != null && mThread.isAlive()) { return; } mThread = new Thread(mStartRunning); mThread.start(); } private Runnable mStartRunning = new Runnable() { @Override public void run() { for(;;) { SystemClock.sleep(100); for (int i = 0; i < mLabels.length; i++) { if (mLocations[i][0] <= getPaddingLeft()) { mDirections[i][0] = DIRECTION_RIGHT; } if (mLocations[i][0] >= getMeasuredWidth() - getPaddingRight() - mTextWidthAndHeight[i][0]) { mDirections[i][0] = DIRECTION_LEFT; } if(mLocations[i][1] <= getPaddingTop() + mTextWidthAndHeight[i][1]) { mDirections[i][1] = DIRECTION_BOTTOM; } if (mLocations[i][1] >= getMeasuredHeight() - getPaddingBottom()) { mDirections[i][1] = DIRECITON_TOP; } int xSpeed = 1; int ySpeed = 2; if(i < mSpeeds.length) { xSpeed = mSpeeds[i][0]; ySpeed = mSpeeds[i][1]; } else { xSpeed = mSpeeds[i-mSpeeds.length][0]; ySpeed = mSpeeds[i-mSpeeds.length][1]; } mLocations[i][0] += mDirections[i][0] == DIRECTION_RIGHT ? xSpeed : -xSpeed; mLocations[i][1] += mDirections[i][1] == DIRECTION_BOTTOM ? ySpeed : -ySpeed; } postInvalidate(); } } }; /** * 初始化位置、方向、label宽高 * 并开启线程 */ private void init() { if(!hasContents()) { return; } int minX = getPaddingLeft(); int minY = getPaddingTop(); int maxX = getMeasuredWidth() - getPaddingRight(); int maxY = getMeasuredHeight() - getPaddingBottom(); Rect textBounds = new Rect(); for (int i = 0; i < mLabels.length; i++) { int[] location = new int[2]; location[0] = minX + (int) (Math.random() * maxX); location[1] = minY + (int) (Math.random() * maxY); mLocations[i] = location; mFontSizes[i] = 15 + (int) (Math.random() * 30); mDirections[i][0] = Math.random() > 0.5 ? DIRECTION_RIGHT : DIRECTION_LEFT; mDirections[i][1] = Math.random() > 0.5 ? DIRECTION_BOTTOM : DIRECITON_TOP; mPaint.setTextSize(mFontSizes[i]); mPaint.getTextBounds(mLabels[i], 0, mLabels[i].length(), textBounds); mTextWidthAndHeight[i][0] = textBounds.width(); mTextWidthAndHeight[i][1] = textBounds.height(); } if(!isStatic) run(); } /** * 是否设置label * @return true or false */ private boolean hasContents() { return mLabels != null && mLabels.length > 0; } /** * 设置labels * @see setLabels(String[] labels) * @param labels */ public void setLabels(List<String> labels) { setLabels((String[]) labels.toArray()); } /** * 设置labels * @param labels */ public void setLabels(String[] labels) { mLabels = labels; mLocations = new int[labels.length][2]; mFontSizes = new int[labels.length]; mDirections = new int[labels.length][2]; mTextWidthAndHeight = new int[labels.length][2]; mSpeeds = new int[labels.length][2]; for(int speed[] : mSpeeds) { speed[0] = speed[1] = 1; } requestLayout(); } /** * 设置配色方案 * @param colorSchema */ public void setColorSchema(int[] colorSchema) { mColorSchema = colorSchema; } /** * 设置每个item的x/y速度 * <p> * speeds.length > labels.length 忽略多余的 * <p> * speeds.length < labels.length 将重复使用 * * @param speeds */ public void setSpeeds(int[][] speeds) { mSpeeds = speeds; } /** * 设置item点击的监听事件 * @param l */ public void setOnItemClickListener(OnItemClickListener l) { getParent().requestDisallowInterceptTouchEvent(true); mListener = l; } /** * item的点击监听事件 */ public interface OnItemClickListener { public void onItemClick(int index, String label); } }