标签云效果很酷,比如最出名的wordle,看看能否在andorid上实现,才发现并不容易,因为我是想做可视化而并不是为了分词,所以感觉难点在布局。这里有两篇博客http://book.51cto.com/art/201108/281730.htm,http://book.51cto.com/art/201108/281728.htm,写的不错第一篇博客提到了在wordle里采用的方法就是一个简单有效的算法——随机贪婪算法。你可以随意地把单词拖放到屏幕中某个期望位置附近,而如果该单词和其他单词存在交叠,就重新再试一次,直到它不再与其他单词交叠为止,第二篇与之不同的是说在调整相应位置时根据一定的路径, 在这里再推荐下http://stackoverflow.com/questions/342687/algorithm-to-implement-a-word-cloud-like-wordle 这个问题wordle原作者亲自回答~~而且还有其他人做的一些。
开始在andorid上下手, 搜了一下并且下载了一个做的还可以的源码 附上链接http://www.apkbus.com/android-232291-1-1.html 它比较好的地方是实现了动画效果,他大体是重写viewgroup 把textview放进去布局,他在找textview大体好像是要记录已经走过的xy。。如果相交,按一定的路径来找下一个可以放置的点,具体代码就我就不班门弄斧而且也没怎么看懂~写的很好,而且有些小地方我借鉴了,比如如何确定字矩形的宽度。我主要的想法是直接重写view 然后让文字直接drawtext。主要是在检测到矩形相交后随机在选择点进行计算(汗,还真是随机贪婪算法,一点路径也没考虑),直到放好所有的。。这就导致并不是所有的时候都能够完美的展现出来(BUG),而且即使摆放好也与想要的效果差好远~~(好累,不想改了真的很麻烦)除了这一点 在改进检测重复即检测碰撞算法上也可以改进,基本上上面那几个博客都有提到,我想用四叉树(http://bbs.9ria.com/thread-148625-1-1.html)改进下,结果效果反而越来越不好,(好心烦 最后我去掉了 我感觉是因为andorid屏幕太小造成的 也可能是我代码造成的),反正在安卓上放的标签也不可能太多,所以对效率影响不大。最后大体就做了这么一个东西,只能叫做东西了好丑,我也不知道200*200能放多少标签,反正自己慢慢试。。
详细代码:
package com.example.visualization; import com.example.tools.CloudItemClickListener; import com.example.tools.TagCloud; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; import android.widget.Toast; public class TagCloudActivity extends Activity { TagCloud cloud; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.tagcloud); cloud = (TagCloud) findViewById(R.id.tag); cloud.addTag("ZZG"); cloud.addTag("LXH"); cloud.addTag("ZLJ"); cloud.addTag("QQ"); cloud.addTag("3D"); cloud.addTag("ZLJ"); cloud.addTag("QQ"); cloud.addTag("3D"); cloud.addTag("ZLJ"); cloud.addTag("QQ"); cloud.addTag("3D"); cloud.addTag("雅诗兰黛"); cloud.addTag("铅笔"); cloud.addTag("QQ"); cloud.addTag("SPYMouse"); cloud.setShowRect(0, 0, 300, 300);// show size cloud.setOnCloudItemClickListener(new CloudItemClickListener() { @Override public void selected(int i) { // TODO Auto-generated method stub Toast.makeText(TagCloudActivity.this, cloud.tags.get(i).getText(), Toast.LENGTH_SHORT).show(); } }); cloud.start(); } }
package com.example.tools; import java.util.ArrayList; import java.util.Random; import com.example.model.Tag; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; public class TagCloud extends View { public ArrayList<Tag> tags = new ArrayList<Tag>(); CloudItemClickListener cloudItemClickListener; Paint paint = new Paint(); int TOP = 0; int BOTTOM = 300; int LEFT = 0; int RIGHT = 300; boolean isDraw = false; boolean isNew = false; // Quadtree quad; public TagCloud(Context context) { super(context); // TODO Auto-generated constructor stub } public TagCloud(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public TagCloud(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub for (int i = 0; i < tags.size(); i++) { Tag tag = tags.get(i); canvas.drawText(tag.getText(), tags.get(i).getRect().left, tags .get(i).getRect().bottom, tags.get(i).getPaint()); } super.onDraw(canvas); } public void setOnCloudItemClickListener(CloudItemClickListener clickListener) { cloudItemClickListener = clickListener; } public void init() { int positionX = (BOTTOM + TOP) / 2; int positionY = (RIGHT + LEFT) / 2; // quad = new Quadtree(0, new Rect(LEFT,TOP,RIGHT,BOTTOM)); // ArrayList<Tag> returnObjects = new ArrayList<Tag>(); Log.e("all", "TOP" + TOP + "BOTTOM" + BOTTOM + "LEFT" + LEFT + "RIGHT" + RIGHT + " ," + tags.size()); for (int i = 0; i < tags.size(); i++) { Random random = new Random(); if (i % 2 == 0) { tags.get(i).setX( positionX + random.nextInt((RIGHT - LEFT) / 20)); tags.get(i).setY( positionY + random.nextInt((BOTTOM - TOP) / 20)); } else { tags.get(i).setX( positionX - random.nextInt((RIGHT - LEFT) / 20)); tags.get(i).setY( positionY - random.nextInt((BOTTOM - TOP) / 20)); } /* * tags.get(i).setX(positionX); tags.get(i).setY(positionY); */ // returnObjects.clear(); // quad.retrieve(returnObjects, tags.get(i).getRect()); int x = 0; Log.e("=======1", tags.get(i).getRect().left + "," + tags.get(i).getRect().top + "," + tags.get(i).getRect().right + "," + tags.get(i).getRect().bottom); // while(x<returnObjects.size()) while (x < i) { // Run collision detection algorithm between objects isNew = false; while (Rect.intersects(tags.get(i).getRect(), tags.get(x) .getRect())) { int ranx = random.nextInt(RIGHT + LEFT); int rany = random.nextInt(BOTTOM + TOP); while (ranx + tags.get(i).getRect().width() > RIGHT || rany + tags.get(i).getRect().height() > BOTTOM || ranx < LEFT || rany < TOP) { ranx = random.nextInt(RIGHT + LEFT); rany = random.nextInt(BOTTOM + TOP); } tags.get(i).setX(ranx); tags.get(i).setY(rany); isNew = true; } if (isNew) { x = -1; } x++; } // quad.insert(tags.get(i)); Log.e("=======2", tags.get(i).getRect().left + "," + tags.get(i).getRect().top + "," + tags.get(i).getRect().right + "," + tags.get(i).getRect().bottom); } isDraw = true; } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { float x = event.getX(); float y = event.getY(); Log.e("Raw x y", "" + event.getX() + "," + event.getY()); for (int i = 0; i < tags.size(); i++) { if (tags.get(i).getRect().contains((int) x, (int) y)) { cloudItemClickListener.selected(i); } } } break; } return super.onTouchEvent(event); } public void addTag(String text) { Tag tag = new Tag(); tag.setText(text); tag.setPaint(); tags.add(tag); } public void setPositionSize() { } public void start() { new Thread(new mythread()).start(); if (isDraw) { postInvalidate(); } } class mythread implements Runnable { @Override public void run() { // TODO Auto-generated method stub init(); } } public void setShowRect(int left, int top, int right, int bottom) { TOP = top; LEFT = left; RIGHT = right; BOTTOM = bottom; } }
package com.example.model; import java.util.Random; import android.graphics.Paint; import android.graphics.Rect; import android.util.Log; public class Tag { public static final int TEXT_SIZE_MAX = 25; public static final int TEXT_SIZE_MIN = 20; String text; int strWidth; Paint paint = new Paint(); float x; float y; int alpha; Random random = new Random(); float scale = 25 + random.nextInt(TEXT_SIZE_MAX - TEXT_SIZE_MIN + 1); public String getText() { return text; } public void setText(String text) { this.text = text; } int ranColor = 0xff000000 | random.nextInt(0x0077ffff); public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public int getAlpha() { return alpha; } public void setAlpha(int alpha) { this.alpha = alpha; } public float getScale() { return scale; } public void setScale(float scale) { this.scale = scale; } public Random getRandom() { return random; } public void setRandom(Random random) { this.random = random; } public int getRanColor() { return ranColor; } public void setRanColor(int ranColor) { this.ranColor = ranColor; } public Paint getPaint() { return paint; } public void setPaint() { paint.setColor(ranColor); paint.setTextSize(scale); strWidth = (int) Math.ceil(paint.measureText(text)); } public Rect getRect() { Rect rect = new Rect((int) x, (int) (y), (int) (x + strWidth), (int) (y + scale)); return rect; } }
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.tools.TagCloud android:id="@+id/tag" android:layout_width="300dp" android:layout_height="300dp" android:background="#BFBFBF"> </com.example.tools.TagCloud> </FrameLayout>
总结:感觉自己诚意不足,屁都没做出来~~先这样吧以后有机会再改进,希望对别人有点帮助,PS:感觉3D标签云反而好做因为他的标签位置可以固定。。