android随机生成圆形云标签效果
程序员文章站
2023-11-21 07:59:27
这个适合用于选择 用户的一些兴趣标签,个性名片等。
package com.dyl.cloudtags;
import java.util.ar...
这个适合用于选择 用户的一些兴趣标签,个性名片等。
package com.dyl.cloudtags; import java.util.arraylist; import java.util.arrays; import java.util.random; import android.app.activity; import android.content.sharedpreferences; import android.os.bundle; import android.view.view; import android.view.view.onclicklistener; import android.widget.edittext; import android.widget.imageview; import android.widget.textview; import android.widget.toast; public class mainactivity extends activity { private keywordsflow keywordsflow; private string[] keywords; public static final string search_history = "search_history"; private arraylist<searchdatapojo> searchitem; private string longhistory; private sharedpreferences sp; private arraylist<string> history; private edittext world_shopping_search_input; private textview world_city_refresh, clear_history; private imageview tosearch; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); initsearchhistory(); refreshtags(); } private void initview() { world_shopping_search_input = (edittext) findviewbyid(r.id.world_shopping_search_input); keywordsflow = (keywordsflow) findviewbyid(r.id.keywordsflow); world_city_refresh = (textview) findviewbyid(r.id.world_city_refresh); world_city_refresh.setonclicklistener(new onclicklistener() { @override public void onclick(view arg0) { refreshtags(); } }); clear_history = (textview) findviewbyid(r.id.clear_history); clear_history.setonclicklistener(new onclicklistener() { @override public void onclick(view arg0) { clearsearchhistory(); } }); tosearch = (imageview) findviewbyid(r.id.tosearch); tosearch.setonclicklistener(new onclicklistener() { @override public void onclick(view arg0) { savesearchhistory(); refreshtags(); } }); } private void refreshtags() { initsearchhistory(); keywordsflow.setduration(800l); keywordsflow.setonitemclicklistener(new onclicklistener() { @override public void onclick(view v) { string keyword = ((textview) v).gettext().tostring();// 获得点击的标签 world_shopping_search_input.settext(keyword); } }); // 添加 feedkeywordsflow(keywordsflow, keywords); keywordsflow.go2show(keywordsflow.animation_in); } private static void feedkeywordsflow(keywordsflow keywordsflow, string[] arr) { random random = new random(); for (int i = 0; i < keywordsflow.max; i++) { int ran = random.nextint(arr.length); string tmp = arr[ran]; keywordsflow.feedkeyword(tmp); } } /** * 读取历史搜索记录 */ private void initsearchhistory() { sp = getsharedpreferences(mainactivity.search_history, 0); longhistory = sp.getstring(mainactivity.search_history, ""); if (!longhistory.equals("")) { keywords = longhistory.split(","); searchitem = new arraylist<searchdatapojo>(); for (int i = 0; i < keywords.length; i++) { searchitem.add(new searchdatapojo().setcontent(keywords[i])); } } else {// 如果sharedpreferences没有值得话,就显示默认的数据 keywords = new string[] { "口味虾", "牛蛙", "火锅", "真功夫", "料理", "密室逃", "天成房", "波比艾" }; } } /* * 保存搜索记录 */ private void savesearchhistory() { string text = world_shopping_search_input.gettext().tostring().trim(); toast.maketext(this, text, toast.length_long).show(); if (!text.equals("") && text != null) { if (text.length() < 1) { return; } sp = getsharedpreferences(search_history, 0); string longhistory = sp.getstring(search_history, ""); string[] tmphistory = longhistory.split(","); history = new arraylist<string>(arrays.aslist(tmphistory)); if (history.size() > 0) { int i; for (i = 0; i < history.size(); i++) { if (text.equals(history.get(i))) { history.remove(i); break; } } history.add(0, text); } if (history.size() > 0) { stringbuilder sb = new stringbuilder(); for (int i = 0; i < history.size(); i++) { sb.append(history.get(i) + ","); } sp.edit().putstring(search_history, sb.tostring()).commit(); } else { sp.edit().putstring(search_history, text + ",").commit(); } clear_history.setvisibility(view.visible); } } // 清除历史数据 private void clearsearchhistory() { searchitem.removeall(searchitem); sp.edit().clear().commit(); toast.maketext(this, "清除历史记录", toast.length_long).show(); } }
用于将控件 设置为圆形 的自定义textview
package com.dyl.cloudtags; import android.content.context; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.paintflagsdrawfilter; import android.util.attributeset; import android.widget.textview; public class circleview extends textview { private paint mbgpaint = new paint(); paintflagsdrawfilter pfd = new paintflagsdrawfilter(0, paint.anti_alias_flag|paint.filter_bitmap_flag); public circleview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); // todo auto-generated constructor stub } public circleview(context context, attributeset attrs) { super(context, attrs); // todo auto-generated constructor stub mbgpaint.setcolor(color.white); mbgpaint.setantialias(true); } public circleview(context context) { super(context); // todo auto-generated constructor stub mbgpaint.setcolor(color.white); mbgpaint.setantialias(true); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { // todo auto-generated method stub super.onmeasure(widthmeasurespec, heightmeasurespec); int measuredwidth = getmeasuredwidth(); int measuredheight = getmeasuredheight(); int max = math.max(measuredwidth, measuredheight); setmeasureddimension(max, max); } @override public void setbackgroundcolor(int color) { // todo auto-generated method stub mbgpaint.setcolor(color); } /** * 设置通知个数显示 * @param text */ public void setnotifitext(int text){ // if(text>99){ // string string = 99+"+"; // settext(string); // return; // } settext(text+""); } @override public void draw(canvas canvas) { // todo auto-generated method stub canvas.setdrawfilter(pfd); canvas.drawcircle(getwidth()/2, getheight()/2, math.max(getwidth(), getheight())/2, mbgpaint); super.draw(canvas); } }
自定义布局 用于动态生成多个 控件 核心类
package com.dyl.cloudtags; import java.util.linkedlist; import java.util.random; import java.util.vector; import android.content.context; import android.graphics.color; import android.graphics.paint; import android.graphics.drawable.gradientdrawable; import android.util.attributeset; import android.view.gravity; import android.view.layoutinflater; import android.view.view; import android.view.viewtreeobserver.ongloballayoutlistener; import android.view.animation.alphaanimation; import android.view.animation.animation; import android.view.animation.animation.animationlistener; import android.view.animation.animationset; import android.view.animation.animationutils; import android.view.animation.interpolator; import android.view.animation.scaleanimation; import android.view.animation.translateanimation; import android.widget.framelayout; public class keywordsflow extends framelayout implements ongloballayoutlistener { public static final int idx_x = 0; public static final int idx_y = 1; public static final int idx_txt_length = 2; public static final int idx_dis_y = 3; /** 由外至内的动画。 */ public static final int animation_in = 1; /** 由内至外的动画。 */ public static final int animation_out = 2; /** 位移动画类型:从外围移动到坐标点。 */ public static final int outside_to_location = 1; /** 位移动画类型:从坐标点移动到外围。 */ public static final int location_to_outside = 2; /** 位移动画类型:从中心点移动到坐标点。 */ public static final int center_to_location = 3; /** 位移动画类型:从坐标点移动到中心点。 */ public static final int location_to_center = 4; public static final long anim_duration = 800l; public static final int max = 12; public static final int text_size_max = 20; public static final int text_size_min = 10; private onclicklistener itemclicklistener; private static interpolator interpolator; private static alphaanimation animalpha2opaque; private static alphaanimation animalpha2transparent; private static scaleanimation animscalelarge2normal, animscalenormal2large, animscalezero2normal, animscalenormal2zero; /** 存储显示的关键字。 */ private vector<string> veckeywords; private int width, height; /** * go2show()中被赋值为true,标识开发人员触发其开始动画显示。<br/> * 本标识的作用是防止在填充keywrods未完成的过程中获取到width和height后提前启动动画。<br/> * 在show()方法中其被赋值为false。<br/> * 真正能够动画显示的另一必要条件:width 和 height不为0。<br/> */ private boolean enableshow; private random random; private int txtanimintype, txtanimouttype; /** 最近一次启动动画显示的时间。 */ private long laststartanimationtime; /** 动画运行时间。 */ private long animduration; private context context; public keywordsflow(context context) { super(context); init(); } public keywordsflow(context context, attributeset attrs) { super(context, attrs); init(); } public keywordsflow(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); init(); } private void init() { laststartanimationtime = 0l; animduration = anim_duration; random = new random(); veckeywords = new vector<string>(max); getviewtreeobserver().addongloballayoutlistener(this); interpolator = animationutils.loadinterpolator(getcontext(), android.r.anim.decelerate_interpolator); animalpha2opaque = new alphaanimation(0.0f, 1.0f); animalpha2transparent = new alphaanimation(1.0f, 0.0f); animscalelarge2normal = new scaleanimation(2, 1, 2, 1); animscalenormal2large = new scaleanimation(1, 2, 1, 2); animscalezero2normal = new scaleanimation(0, 1, 0, 1); animscalenormal2zero = new scaleanimation(1, 0, 1, 0); } public long getduration() { return animduration; } public void setduration(long duration) { animduration = duration; } public boolean feedkeyword(string keyword) { boolean result = false; if (veckeywords.size() < max) { result = veckeywords.add(keyword); } return result; } /** * 开始动画显示。<br/> * 之前已经存在的textview将会显示退出动画。<br/> * * @return 正常显示动画返回true;反之为false。返回false原因如下:<br/> * 1.时间上不允许,受laststartanimationtime的制约;<br/> * 2.未获取到width和height的值。<br/> */ public boolean go2show(int animtype) { if (system.currenttimemillis() - laststartanimationtime > animduration) { enableshow = true; if (animtype == animation_in) { txtanimintype = outside_to_location; txtanimouttype = location_to_center; } else if (animtype == animation_out) { txtanimintype = center_to_location; txtanimouttype = location_to_outside; } disapper(); boolean result = show(); return result; } return false; } private void disapper() { int size = getchildcount(); for (int i = size - 1; i >= 0; i--) { final circleview txv = (circleview) getchildat(i); if (txv.getvisibility() == view.gone) { removeview(txv); continue; } framelayout.layoutparams layparams = (layoutparams) txv .getlayoutparams(); int[] xy = new int[] { layparams.leftmargin, layparams.topmargin, txv.getwidth() }; animationset animset = getanimationset(xy, (width >> 1), (height >> 1), txtanimouttype); txv.startanimation(animset); animset.setanimationlistener(new animationlistener() { public void onanimationstart(animation animation) { } public void onanimationrepeat(animation animation) { } public void onanimationend(animation animation) { txv.setonclicklistener(null); txv.setclickable(false); txv.setvisibility(view.gone); } }); } } private boolean show() { if (width > 0 && height > 0 && veckeywords != null && veckeywords.size() > 0 && enableshow) { enableshow = false; laststartanimationtime = system.currenttimemillis(); int xcenter = width >> 1, ycenter = height >> 1; int size = veckeywords.size(); int xitem = width / size, yitem = height / size; linkedlist<integer> listx = new linkedlist<integer>(), listy = new linkedlist<integer>(); for (int i = 0; i < size; i++) { // 准备随机候选数,分别对应x/y轴位置 listx.add(i * xitem); listy.add(i * yitem + (yitem >> 2)); } linkedlist<circleview> listtxttop = new linkedlist<circleview>(); linkedlist<circleview> listtxtbottom = new linkedlist<circleview>(); for (int i = 0; i < size; i++) { string keyword = veckeywords.get(i); // 随机位置,糙值 int xy[] = randomxy(random, listx, listy, xitem); // 实例化textview final circleview txv = new circleview(getcontext()); txv.setbackgroundresource(r.drawable.text_view_border); txv.setgravity(gravity.center); txv.setonclicklistener(itemclicklistener); txv.settext(keyword); txv.settextcolor(color.white); txv.setpadding(8, 6, 8, 6); txv.setsingleline(true); int r = random.nextint(256); int g= random.nextint(256); int b = random.nextint(256); int mcolor = color.rgb(r, g, b); gradientdrawable mygrad = (gradientdrawable)txv.getbackground(); mygrad.setcolor(mcolor); // txv.setbackgroundcolor(mcolor); // 获取文本长度 paint paint = txv.getpaint(); int strwidth = (int) math.ceil(paint.measuretext(keyword)); xy[idx_txt_length] = strwidth; // 第一次修正:修正x坐标 if (xy[idx_x] + strwidth > width - (xitem >> 1)) { int basex = width - strwidth; // 减少文本右边缘一样的概率 xy[idx_x] = basex - xitem + random.nextint(xitem >> 1); } else if (xy[idx_x] == 0) { // 减少文本左边缘一样的概率 xy[idx_x] = math.max(random.nextint(xitem), xitem / 3); } xy[idx_dis_y] = math.abs(xy[idx_y] - ycenter); txv.settag(xy); if (xy[idx_y] > ycenter) { listtxtbottom.add(txv); } else { listtxttop.add(txv); } } attach2screen(listtxttop, xcenter, ycenter, yitem); attach2screen(listtxtbottom, xcenter, ycenter, yitem); return true; } return false; } /** 修正textview的y坐标将将其添加到容器上。 */ private void attach2screen(linkedlist<circleview> listtxt, int xcenter, int ycenter, int yitem) { int size = listtxt.size(); sortxylist(listtxt, size); for (int i = 0; i < size; i++) { circleview txv = listtxt.get(i); int[] ixy = (int[]) txv.gettag(); // 第二次修正:修正y坐标 int ydistance = ixy[idx_y] - ycenter; // 对于最靠近中心点的,其值不会大于yitem<br/> // 对于可以一路下降到中心点的,则该值也是其应调整的大小<br/> int ymove = math.abs(ydistance); inner: for (int k = i - 1; k >= 0; k--) { int[] kxy = (int[]) listtxt.get(k).gettag(); int startx = kxy[idx_x]; int endx = startx + kxy[idx_txt_length]; // y轴以中心点为分隔线,在同一侧 if (ydistance * (kxy[idx_y] - ycenter) > 0) { if (isxmixed(startx, endx, ixy[idx_x], ixy[idx_x] + ixy[idx_txt_length])) { int tmpmove = math.abs(ixy[idx_y] - kxy[idx_y]); if (tmpmove > yitem) { ymove = tmpmove; } else if (ymove > 0) { // 取消默认值。 ymove = 0; } break inner; } } } if (ymove > yitem) { int maxmove = ymove - yitem; int randommove = random.nextint(maxmove); int realmove = math.max(randommove, maxmove >> 1) * ydistance / math.abs(ydistance); ixy[idx_y] = ixy[idx_y] - realmove; ixy[idx_dis_y] = math.abs(ixy[idx_y] - ycenter); // 已经调整过前i个需要再次排序 sortxylist(listtxt, i + 1); } framelayout.layoutparams layparams = new framelayout.layoutparams( framelayout.layoutparams.wrap_content, framelayout.layoutparams.wrap_content); layparams.gravity = gravity.left | gravity.top; layparams.leftmargin = ixy[idx_x]; layparams.topmargin = ixy[idx_y]; addview(txv, layparams); // 动画 animationset animset = getanimationset(ixy, xcenter, ycenter, txtanimintype); txv.startanimation(animset); } } public animationset getanimationset(int[] xy, int xcenter, int ycenter, int type) { animationset animset = new animationset(true); animset.setinterpolator(interpolator); if (type == outside_to_location) { animset.addanimation(animalpha2opaque); animset.addanimation(animscalelarge2normal); translateanimation translate = new translateanimation((xy[idx_x] + (xy[idx_txt_length] >> 1) - xcenter) << 1, 0, (xy[idx_y] - ycenter) << 1, 0); animset.addanimation(translate); } else if (type == location_to_outside) { animset.addanimation(animalpha2transparent); animset.addanimation(animscalenormal2large); translateanimation translate = new translateanimation(0, (xy[idx_x] + (xy[idx_txt_length] >> 1) - xcenter) << 1, 0, (xy[idx_y] - ycenter) << 1); animset.addanimation(translate); } else if (type == location_to_center) { animset.addanimation(animalpha2transparent); animset.addanimation(animscalenormal2zero); translateanimation translate = new translateanimation(0, (-xy[idx_x] + xcenter), 0, (-xy[idx_y] + ycenter)); animset.addanimation(translate); } else if (type == center_to_location) { animset.addanimation(animalpha2opaque); animset.addanimation(animscalezero2normal); translateanimation translate = new translateanimation( (-xy[idx_x] + xcenter), 0, (-xy[idx_y] + ycenter), 0); animset.addanimation(translate); } animset.setduration(animduration); return animset; } /** * 根据与中心点的距离由近到远进行冒泡排序。 * * @param endidx * 起始位置。 * @param txtarr * 待排序的数组。 * */ private void sortxylist(linkedlist<circleview> listtxt, int endidx) { for (int i = 0; i < endidx; i++) { for (int k = i + 1; k < endidx; k++) { if (((int[]) listtxt.get(k).gettag())[idx_dis_y] < ((int[]) listtxt .get(i).gettag())[idx_dis_y]) { circleview itmp = listtxt.get(i); circleview ktmp = listtxt.get(k); listtxt.set(i, ktmp); listtxt.set(k, itmp); } } } } /** a线段与b线段所代表的直线在x轴映射上是否有交集。 */ private boolean isxmixed(int starta, int enda, int startb, int endb) { boolean result = false; if (startb >= starta && startb <= enda) { result = true; } else if (endb >= starta && endb <= enda) { result = true; } else if (starta >= startb && starta <= endb) { result = true; } else if (enda >= startb && enda <= endb) { result = true; } return result; } //得到随机坐标 private int[] randomxy(random ran, linkedlist<integer> listx, linkedlist<integer> listy, int xitem) { int[] arr = new int[4]; arr[idx_x] = listx.remove(ran.nextint(listx.size())); arr[idx_y] = listy.remove(ran.nextint(listy.size())); return arr; } public void ongloballayout() { int tmpw = getwidth(); int tmph = getheight(); if (width != tmpw || height != tmph) { width = tmpw; height = tmph; show(); } } public vector<string> getkeywords() { return veckeywords; } public void rubkeywords() { veckeywords.clear(); } /** 直接清除所有的textview。在清除之前不会显示动画。 */ public void ruballviews() { removeallviews(); } public void setonitemclicklistener(onclicklistener listener) { itemclicklistener = listener; } }
package com.dyl.cloudtags; /** * 搜索记录 * @author dengyalan * */ public class searchdatapojo { private string content = ""; public string getcontent() { return content; } public searchdatapojo setcontent(string content) { this.content = content; return this; } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。