模仿百度红包福袋界面实例代码
程序员文章站
2024-03-31 19:55:04
新年到新年到,红包抢不停。在我抢红包的时候意外的发现了百度的福袋界面挺不错的,于是抽时间专门写篇文章来完成百度红包界面吧。
当然啦,这其实就是解锁界面的进化版本。不过其包...
新年到新年到,红包抢不停。在我抢红包的时候意外的发现了百度的福袋界面挺不错的,于是抽时间专门写篇文章来完成百度红包界面吧。
当然啦,这其实就是解锁界面的进化版本。不过其包含的知识点还是挺多的,写篇博文记录一下看看具体有哪些技术点啦。看看百度的效果图:
1.编程思路
看看界面,不难发现,其就是一个放入九张图片的容器,绘制其实可以在其上面另创建一个透明view负责绘制线与圆圈。下面我们将介绍一下实现过程。
㈠自定义viewgroup
我们知道,自定义viewgroup一定需要实现其onlayout()方法。该方法是设置子view位置与尺寸的时候调用。还有一个onmeasure()方法,该方法是测量view及其内容来确定view的宽度和高度。
㈡存储其点与圆的位置及绘制参数
当重回界面的时候,是不会保存上一次绘制界面的内容,必须存储以备重绘时候绘制到界面
㈢简单的缩放动画
㈣自定义view实现绘制界面
㈤绘制完成时,清除界面绘制内容,并且保证不连接重复图片
下面我们将完成这些步骤。
2.自定义viewgroup
开始的任务就是将九张图片平均分布到图片的位置,显示在手机界面中。其代码如下:
public class lyjviewgroup extends viewgroup implements lyjgesturedrawline.onanimationcallback{ /** * 每个点区域的宽度 */ private int childwidth; /*** * 上下文 */ private context context; /*** * 保存图片点的位置 */ private list<lyjgesturepoint> list; /*** * 创建view使其在viewgroup之上。 */ private lyjgestureview gesturedrawline; private int basenum = 5; public lyjviewgroup(context context) { super(context); this.context = context; this.list = new arraylist<>(); displaymetrics metric = new displaymetrics(); ((activity) context).getwindowmanager().getdefaultdisplay().getmetrics(metric); childwidth = metric.widthpixels / 3; // 屏幕宽度(像素) addchild(); // 初始化一个可以画线的view gesturedrawline = new lyjgestureview(context, list); gesturedrawline.setanimationcallback(this); } public void setparentview(viewgroup parent){ // 得到屏幕的宽度 displaymetrics metric = new displaymetrics(); ((activity) context).getwindowmanager().getdefaultdisplay().getmetrics(metric); int width = metric.widthpixels; layoutparams layoutparams = new layoutparams(width, width); this.setlayoutparams(layoutparams); gesturedrawline.setlayoutparams(layoutparams); parent.addview(this); parent.addview(gesturedrawline); } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getchildcount(); i++) { //第几行 int rowspan = i / 3; //第几列 int column = i % 3; android.view.view v = getchildat(i); v.layout(column * childwidth + childwidth / basenum, rowspan * childwidth + childwidth / basenum, column * childwidth + childwidth - childwidth / basenum, rowspan * childwidth + childwidth - childwidth / basenum); } } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); // 遍历设置每个子view的大小 for (int i = 0; i < getchildcount(); i++) { view v = getchildat(i); v.measure(widthmeasurespec, heightmeasurespec); } } private void addchild() { for (int i = 0; i < 9; i++) { imageview image = new imageview(context); image.setbackgroundresource(r.drawable.marker); this.addview(image); invalidate(); // 第几行 int rowspan = i / 3; // 第几列 int column = i % 3; // 定义点的左上角与右下角的坐标 int leftx = column * childwidth + childwidth / basenum; int topy = rowspan * childwidth + childwidth / basenum; int rightx = column * childwidth + childwidth - childwidth / basenum; int bottomy = rowspan * childwidth + childwidth - childwidth / basenum; lyjgesturepoint p = new lyjgesturepoint(leftx, topy, rightx,bottomy,i); this.list.add(p); } } @override public void startanimationimage(int i) { animation animation= animationutils.loadanimation(getcontext(), r.anim.gridlayout_child_scale_anim); getchildat(i).startanimation(animation); } }
3.自定义点类
顾名思义,就是为了获取点的相关的属性,其中基础属性图片左上角坐标与右下角坐标,计算图片中心位置以便获取图片中心点。状态标记,表示该点是否绘制到图片。下面是其实体类:
public class lyjgesturepoint { private point pointlefttop;//左上角坐标 private point pointrightbottom;//右下角坐标 private int centerx;//图片中心点x坐标 private int centery;//图片中心点y坐标 private int pointstate;//是否点击了该图片 private int num; public int getnum() { return num; } public int getpointstate() { return pointstate; } public void setpointstate(int pointstate) { this.pointstate = pointstate; } public point getpointlefttop() { return pointlefttop; } public point getpointrightbottom() { return pointrightbottom; } public lyjgesturepoint(int left,int top,int right,int bottom,int i){ this.pointlefttop=new point(left,top); this.pointrightbottom=new point(right,bottom); this.num=i; } public int getcenterx() { this.centerx=(this.pointlefttop.x+this.pointrightbottom.x)/2; return centerx; } public int getcentery() { this.centery=(this.pointlefttop.y+this.pointrightbottom.y)/2; return centery; } }
4.自定义圆类
这个类较简单就三个属性而已(圆中心点坐标及半径),代码如下:
public class lyjcirclepoint { private int roundx;//圆中心点x坐标 private int roundy;//圆中心点y坐标 private int radiu;//圆半径 public int getradiu() { return radiu; } public int getroundx() { return roundx; } public int getroundy() { return roundy; } public lyjcirclepoint(int roundx,int roundy,int radiu){ this.roundx=roundx; this.roundy=roundy; this.radiu=radiu; } }
5.实现自定义绘制类view
代码如下:
public class lyjgestureview extends android.view.view { /*** * 声明直线画笔 */ private paint paint; /*** * 声明圆圈画笔 */ private paint circlepaint; /*** * 画布 */ private canvas canvas; /*** * 位图 */ private bitmap bitmap; /*** * 装有各个view坐标的集合,用于判断点是否在其中 */ private list<lyjgesturepoint> list; /*** * 记录画过的线 */ private list<pair<lyjgesturepoint, lyjgesturepoint>> linelist; /*** * 记录画过的圆 */ private list<lyjcirclepoint> circlepoints; /** * 手指当前在哪个point内 */ private lyjgesturepoint currentpoint; /*** * 手指按下动画 */ private onanimationcallback animationcallback; public interface onanimationcallback{ public void startanimationimage(int i); } public void setanimationcallback(onanimationcallback animationcallback) { this.animationcallback = animationcallback; } public lyjgestureview(context context, list<lyjgesturepoint> list){ super(context); log.i(getclass().getname(), "gesturedrawline"); paint = new paint(paint.dither_flag);// 创建一个画笔 circlepaint=new paint(paint.dither_flag); displaymetrics metric = new displaymetrics(); ((activity)context).getwindowmanager().getdefaultdisplay().getmetrics(metric); log.i(getclass().getname(), "widthpixels" + metric.widthpixels); log.i(getclass().getname(), "heightpixels" + metric.heightpixels); bitmap = bitmap.createbitmap(metric.widthpixels, metric.heightpixels, bitmap.config.argb_8888); // 设置位图的宽高 canvas = new canvas(); canvas.setbitmap(bitmap); paint.setstyle(paint.style.stroke);// 设置非填充 paint.setstrokewidth(20);// 笔宽20像素 paint.setcolor(color.rgb(245, 142, 33));// 设置默认连线颜色 paint.setantialias(true);// 不显示锯齿 circlepaint.setstyle(paint.style.fill); circlepaint.setstrokewidth(1); circlepaint.setantialias(true); circlepaint.setcolor(color.rgb(245, 142, 33)); this.list = list; this.linelist = new arraylist<>(); this.circlepoints=new arraylist<>(); } @override public boolean ontouchevent(motionevent event) { switch (event.getaction()){ case motionevent.action_down: // 判断当前点击的位置是处于哪个点之内 currentpoint = getpointat((int) event.getx(), (int) event.gety()); if (currentpoint != null) { currentpoint.setpointstate(constants.point_state_selected); this.animationcallback.startanimationimage(currentpoint.getnum()); canvas.drawcircle(currentpoint.getcenterx(), currentpoint.getcentery(), 20, circlepaint); circlepoints.add(new lyjcirclepoint(currentpoint.getcenterx(),currentpoint.getcentery(),20)); } invalidate(); break; case motionevent.action_move: clearscreenanddrawlist(); // 得到当前移动位置是处于哪个点内 lyjgesturepoint pointat = getpointat((int) event.getx(), (int) event.gety()); if (currentpoint == null && pointat == null) {//你把手指按在屏幕滑动,如果终点与起点都不图片那么返回 return true; } else {// 代表用户的手指移动到了点上 if (currentpoint == null) {// 先判断当前的point是不是为null // 如果为空,那么把手指移动到的点赋值给currentpoint currentpoint = pointat; // 把currentpoint这个点设置选中状态; currentpoint.setpointstate(constants.point_state_selected); } } //如果移动到的点不为图片区域或者移动到自己的地方,或者该图片已经为选中状态,直接画直线就可以了 if(pointat == null || currentpoint.equals(pointat) || constants.point_state_selected == pointat.getpointstate()){ canvas.drawcircle(currentpoint.getcenterx(), currentpoint.getcentery(), 20, circlepaint); circlepoints.add(new lyjcirclepoint(currentpoint.getcenterx(), currentpoint.getcentery(), 20)); canvas.drawline(currentpoint.getcenterx(), currentpoint.getcentery(), event.getx(), event.gety(), paint); }else{//其他情况画两点相连直线,并且保存绘制圆与直线,并调用按下图片的缩放动画 canvas.drawcircle(pointat.getcenterx(),pointat.getcentery(),20,circlepaint); circlepoints.add(new lyjcirclepoint(pointat.getcenterx(), pointat.getcentery(), 20)); this.animationcallback.startanimationimage(pointat.getnum()); pointat.setpointstate(constants.point_state_selected); canvas.drawline(currentpoint.getcenterx(), currentpoint.getcentery(), pointat.getcenterx(), pointat.getcentery(), paint); pair<lyjgesturepoint, lyjgesturepoint> pair = new pair<>(currentpoint, pointat); linelist.add(pair); currentpoint=pointat;//设置选中点为当前点。 } invalidate();//重绘 break; case motionevent.action_up: clearscreenanddrawlist();//防止多出一条没有终点的直线 new handler().postdelayed(new clearlinerunnable(), 1000);//1秒后清空绘制界面 invalidate();//重绘 break; default: break; } return true; } class clearlinerunnable implements runnable { public void run() { // 清空保存点与圆的集合 linelist.clear(); circlepoints.clear(); // 重新绘制界面 clearscreenanddrawlist(); for (lyjgesturepoint p : list) { //设置其为初始化不选中状态 p.setpointstate(constants.point_state_normal); } invalidate(); } } /** * 通过点的位置去集合里面查找这个点是包含在哪个point里面的 * * @param x * @param y * @return 如果没有找到,则返回null,代表用户当前移动的地方属于点与点之间 */ private lyjgesturepoint getpointat(int x, int y) { for (lyjgesturepoint point : list) { // 先判断点是否在图片的x坐标内 int leftx = point.getpointlefttop().x; int rightx = point.getpointrightbottom().x; if (!(x >= leftx && x < rightx)) { // 如果为假,则跳到下一个对比 continue; } //在判断点是否在图片的y坐标内 int topy = point.getpointlefttop().y; int bottomy = point.getpointrightbottom().y; if (!(y >= topy && y < bottomy)) { // 如果为假,则跳到下一个对比 continue; } // 如果执行到这,那么说明当前点击的点的位置在遍历到点的位置这个地方 return point; } return null; } /** * 清掉屏幕上所有的线,然后画出集合里面的线 */ private void clearscreenanddrawlist() { canvas.drawcolor(color.transparent, porterduff.mode.clear); for (pair<lyjgesturepoint, lyjgesturepoint> pair : linelist) { canvas.drawline(pair.first.getcenterx(), pair.first.getcentery(), pair.second.getcenterx(), pair.second.getcentery(), paint);// 画线 } for(lyjcirclepoint lyjcirclepoint : circlepoints){ canvas.drawcircle(lyjcirclepoint.getroundx(),lyjcirclepoint.getroundy(), lyjcirclepoint.getradiu(),circlepaint); } } //绘制用bitmap创建出来的画布 @override protected void ondraw(canvas canvas) { canvas.drawbitmap(bitmap, 0, 0, null); } }
这样就可以得到如下界面效果(当然反编译百度钱包,并没有百度钱包中的图片,只好随便找了一张图片):
下一篇: PHP从数组中删除元素的四种方法实例