手把手带你画一个漂亮蜂窝view Android自定义view

时间:2022-09-09 18:04:49

手把手带你画一个漂亮蜂窝view Android自定义view

标签: Android自定义view 1033人阅读 评论(4)收藏 举报 手把手带你画一个漂亮蜂窝view Android自定义view分类:

上一篇做了一个水波纹view  不知道大家有没有动手试试呢点击打开链接


这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代。写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义view需求。


转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50554058


本文是和代码同步写的。也就是说在写文章的时候才敲的代码。这样会显得文章有些许混乱。但是我想这样记录下来,一个自定义view的真正的制作过程,是一点一点,一步步跟着思路的改变,完善的。不可能一下子就做出一个完整的view。。技术也是这样,不可能一步登天。都是一步一步的积累。


另外,每一篇博客都是建立在之前博客的基础知识上的,如果你刚接触自定义view。可以来说说自定义view简单学习的方式这里看我以前的文章。记录了我学习自定义view的过程,而且前几篇博客或多或少犯了一些错误。这里我并不想改正博文中的错误,因为些错误是大家经常会犯的,后来的博客都有指出这些错误,以及不再犯,这是一个学习的过程。所以我想把错误的经历记录下来。等成为高手 回头看看当年的自己是多么菜。。也会有成就感。。



老规矩效果图如下:

手把手带你画一个漂亮蜂窝view Android自定义view

首先画一个六边形,画之前来计算一下六边形的相关知识:

手把手带你画一个漂亮蜂窝view Android自定义view

假设一个正六边形的边长为a  ,因为每个角都是120°  所以可得高为根号三a  ,如图所示。


有了这些信息我们就可以绘制一个六边形出来,如下:


[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. float height = (float) (Math.sqrt(3)*mLength);  
  2.        mPath.moveTo(mLength/2,0);  
  3.        mPath.lineTo(0,height/2);  
  4.        mPath.lineTo(mLength/2,height);  
  5.        mPath.lineTo((float) (mLength*1.5),height);  
  6.        mPath.lineTo(2*mLength,height/2);  
  7.        mPath.lineTo((float) (mLength*1.5),0);  
  8.        mPath.lineTo(mLength/2,0);  
  9.        mPath.close();  

绘制效果:


手把手带你画一个漂亮蜂窝view Android自定义view


然后将其根据一个偏移量进行平移,就可以用循环绘制出多个六边形


这里offset是偏移量,紧挨着的话应该是偏移一个六边形的宽,宽由上图可知为 a/2+a+a/2 即 2a; 

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. for(int i = 0 ; i < 3;i++) {  
  2.            int offset = mLength * 2 * i;  
  3.            mPath.moveTo(mLength / 2 + offset, 0);  
  4.            mPath.lineTo(0 + offset, height / 2);  
  5.            mPath.lineTo(mLength / 2 + offset, height);  
  6.            mPath.lineTo((float) (mLength * 1.5) + offset, height);  
  7.            mPath.lineTo(2 * mLength + offset, height / 2);  
  8.            mPath.lineTo((float) (mLength * 1.5)+offset, 0);  
  9.            mPath.lineTo(mLength / 2+offset, 0);  
  10.            mPath.close();  
  11.        }  

发现效果如下

手把手带你画一个漂亮蜂窝view Android自定义view

这不对啊,很奇怪啊。。 底下空出来的一个三角形放不下我们的第二行啊。。

那么怎么办呢。。 加大offset!  加大多少呢。。 应该多增加一个边长。。这样就正好留空了。 来试试

手把手带你画一个漂亮蜂窝view Android自定义view


现在来准备画第二行....

发现我们之前path的坐标都是相对写死的。。 所以要回过头改一下,改成给定一个起点,就可以绘制出一个六边形,经过计算,得出下图

手把手带你画一个漂亮蜂窝view Android自定义view

这里a代表边长。

改完之后的代码是:

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. float height = (float) (Math.sqrt(3)*mLength);  
  2.        for(int i = 0 ; i < 3;i++) {  
  3.   
  4.            //横坐标偏移量  
  5.            int offset = mLength * 3 * i ;  
  6.            //左上角的x  
  7.            int x = mLength/2 + offset;  
  8.            int y = 0;  
  9.   
  10.            //根据左上角一点 绘制整个正六边形  
  11.            mPath.moveTo(x, y);  
  12.            mPath.lineTo(x -mLength/2, height / 2 + y);  
  13.            mPath.lineTo(x, height+y);  
  14.            mPath.lineTo(x + mLength, height +y);  
  15.            mPath.lineTo((float) (x + 1.5*mLength), height / 2+y);  
  16.            mPath.lineTo(x + mLength, y);  
  17.            mPath.lineTo(x, y);  
  18.            mPath.close();  
  19.        }  

绘制出来的效果是一样的。但是方法以及变了。

然后来画第二行,第二行起点的path应该在这里

手把手带你画一个漂亮蜂窝view Android自定义view

坐标是: 2a , height/2  这里的偏移量不变。

首先将画path的方法提取出来(as快捷键ctrl + alt + m)

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. //根据左上角一点 绘制整个正六边形  
  2.    private void getPath(float height, float x, float y) {  
  3.        mPath.moveTo(x, y);  
  4.        mPath.lineTo(x -mLength/2, height / 2 + y);  
  5.        mPath.lineTo(x, height+y);  
  6.        mPath.lineTo(x + mLength, height +y);  
  7.        mPath.lineTo((float) (x + 1.5*mLength), height / 2+y);  
  8.        mPath.lineTo(x + mLength, y);  
  9.        mPath.lineTo(x, y);  
  10.        mPath.close();  
  11.    }  
然后再给个循环,来绘制第二行的六边形[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. for(int i = 0;i<2;i++){  
  2.   
  3.     float offset = mLength * 3 * i ;  
  4.     float x = mLength*2 + offset;  
  5.     float y = height/2;  
  6.   
  7.     getPath(height,x,y);  
  8.   
  9.   
  10. }  
  11.   
  12.   
  13.   
  14.   
  15.   
  16. canvas.drawPath(mPath,mPaint);  
得到如下的效果。

手把手带你画一个漂亮蜂窝view Android自定义view

现在ondraw的全部代码如下:

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. @Override  
  2.   protected void onDraw(Canvas canvas) {  
  3.       mPaint.setColor(Color.parseColor("#FFBB33"));  
  4.       //正六边形的高  
  5.       float height = (float) (Math.sqrt(3)*mLength);  
  6.       for(int i = 0 ; i < 3;i++) {  
  7.   
  8.           //横坐标偏移量  
  9.           float offset = mLength * 3 * i ;  
  10.           //左上角的x  
  11.           float x = mLength/2 + offset;  
  12.           float y = 0;  
  13.   
  14.           getPath(height, x, y);  
  15.       }  
  16.   
  17.       canvas.drawPath(mPath,mPaint);  
  18.       mPath.reset();  
  19.   
  20.       mPaint.setColor(Color.parseColor("#AA66CC"));  
  21.   
  22.       for(int i = 0;i<2;i++){  
  23.   
  24.           float offset = mLength * 3 * i ;  
  25.           float x = mLength*2 + offset;  
  26.           float y = height/2;  
  27.   
  28.           getPath(height,x,y);  
  29.   
  30.   
  31.       }  
  32.   
  33.       canvas.drawPath(mPath,mPaint);  
  34.   }  

接下来对每行的个数进行一下控制。[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. //每行的个数  
  2.   private int mColumnsCount = 3;  
  3.   
  4.   //行数  
  5.   private int mLineCount = 3;  

对应的循环也改变,最外面套一个大循环,来控制多行绘制[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1.  for (int j = 0; j < mLineCount; j++) {  
  2.          if(j%2 == 0)  绘制奇数行 else 绘制偶数行  
  3.   
  4. }  
现在整个ondraw如下。
[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. @Override  
  2.     protected void onDraw(Canvas canvas) {  
  3.         //正六边形的高  
  4.         float height = (float) (Math.sqrt(3) * mLength);  
  5.   
  6.         for (int j = 0; j < mLineCount; j++) {  
  7.             if (j % 2 == 0) {  
  8.                 mPaint.setColor(Color.parseColor("#FFBB33"));  
  9.   
  10.                 for (int i = 0; i < mColumnsCount; i++) {  
  11.   
  12.                     //横坐标偏移量  
  13.                     float offset = mLength * 3 * i;  
  14.                     //左上角的x  
  15.                     float x = mLength / 2 + offset;  
  16.                     float y = j * height / 2;  
  17.   
  18.                     getPath(height, x, y);  
  19.   
  20.   
  21.                 }  
  22.                 canvas.drawPath(mPath, mPaint);  
  23.                 mPath.reset();  
  24.             } else {  
  25.   
  26.                 mPaint.setColor(Color.parseColor("#AA66CC"));  
  27.   
  28.                 for (int i = 0; i < mColumnsCount; i++) {  
  29.   
  30.                     float offset = mLength * 3 * i;  
  31.                     float x = mLength * 2 + offset;  
  32.                     float y = (height / 2) * j;  
  33.   
  34.                     getPath(height, x, y);  
  35.   
  36.   
  37.                 }  
  38.   
  39.                 canvas.drawPath(mPath, mPaint);  
  40.                 mPath.reset();  
  41.             }  
  42.   
  43.   
  44.         }  
  45.   
  46.   
  47.     }  

手把手带你画一个漂亮蜂窝view Android自定义view

好像颜色一样就不好看了。。那我们来动态改变一下颜色..

添加一个属性list来存放color

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. private ArrayList<Integer> mColorList;  
[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. mColorList = new ArrayList<>();  
  2.   
  3.        mColorList.add(Color.parseColor("#33B5E5"));  
  4.   
  5.        mColorList.add(Color.parseColor("#AA66CC"));  
  6.   
  7.        mColorList.add(Color.parseColor("#99CC00"));  
  8.   
  9.        mColorList.add(Color.parseColor("#FFBB33"));  
  10.   
  11.        mColorList.add(Color.parseColor("#FF4444"));  


在循环中,取出颜色值[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. for (int j = 0; j < mLineCount; j++) {  
  2.   
  3.            mPaint.setColor(mColorList.get(j));  


效果如下:

手把手带你画一个漂亮蜂窝view Android自定义view

嗯。。看起来像一点样子了。。。 给中间加点文字吧。。

先给每个蜂窝编号

手把手带你画一个漂亮蜂窝view Android自定义view


按上面的循环   j为行数  i为列数  

研究规律发现   编号等于 j*3 + i 

我们有六边形左上角的坐标xy 可以轻易的计算出中心坐标





手把手带你画一个漂亮蜂窝view Android自定义view


这些都有了。开一个list存放中间的文字:

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. //存放文字的list  
  2. private ArrayList<String> mTextList ;  


在初始化的时候给添加点数据[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. mTextList = new ArrayList<>();  
  2.       for(int i =0;i<mLineCount*mColumnsCount;i++){  
  3.           mTextList.add("wing "+i);  
  4.       }  
  5.   
  6.       mTextPaint = new Paint();  
  7.       mTextPaint.setTextSize(20);  


绘制文字: 这里要注意他和path的绘制顺序,如果path后绘制则会覆盖掉文字[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. float txtLength = mTextPaint.measureText(mTextList.get(txtId));  
  2.                    canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);  


下面是全部的ondraw

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1.   @Override  
  2.     protected void onDraw(Canvas canvas) {  
  3.         //正六边形的高  
  4.         float height = (float) (Math.sqrt(3) * mLength);  
  5.         for (int j = 0; j < mLineCount; j++) {  
  6.   
  7.             mPaint.setColor(mColorList.get(j));  
  8.   
  9.             if (j % 2 == 0) {  
  10.   
  11. //                mPaint.setColor(Color.parseColor("#FFBB33"));  
  12.   
  13.                 for (int i = 0; i < mColumnsCount; i++) {  
  14.                     int txtId = j*3 +i;  
  15.                     //横坐标偏移量  
  16.                     float offset = mLength * 3 * i;  
  17.                     //左上角的x  
  18.                     float x = mLength / 2 + offset;  
  19.                     float y = j * height / 2;  
  20.   
  21.   
  22.                     mPath.reset();  
  23.                     getPath(height, x, y);  
  24.   
  25.                     canvas.drawPath(mPath, mPaint);  
  26.                     float txtLength = mTextPaint.measureText(mTextList.get(txtId));  
  27.                     canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);  
  28.   
  29.                 }  
  30.             } else {  
  31.   
  32. //                mPaint.setColor(Color.parseColor("#AA66CC"));  
  33.   
  34.                 for (int i = 0; i < mColumnsCount; i++) {  
  35.   
  36.                     int txtId = j*3 +i;  
  37.                     float offset = mLength * 3 * i;  
  38.                     float x = mLength * 2 + offset;  
  39.                     float y = (height / 2) * j;  
  40.                     mPath.reset();  
  41.                     getPath(height, x, y);  
  42.                     canvas.drawPath(mPath, mPaint);  
  43.                     float txtLength = mTextPaint.measureText(mTextList.get(txtId));  
  44.                     canvas.drawText(mTextList.get(txtId),x+mLength/2-txtLength/2,y+height/2+5, mTextPaint);  
  45.   
  46.                 }  
  47.   
  48.   
  49.   
  50.             }  
  51.   
  52.   
  53.         }  
  54.   
  55.   
  56.     }  

现在的效果图如下:

手把手带你画一个漂亮蜂窝view Android自定义view

好,那现在让他灵活一点。添加各种set方法,比如行数啊 列数啊  边长啊 文字内容啊 颜色啊之类的。

[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1. /** 
  2.    * 设置列数 
  3.    * @param mColumnsCount 
  4.    */  
  5.   public void setColumnsCount(int mColumnsCount) {  
  6.       this.mColumnsCount = mColumnsCount;  
  7.       invalidate();  
  8.   }  
  9.   
  10.   /** 
  11.    * 设置行数 
  12.    * @param mLineCount 
  13.    */  
  14.   public void setLineCount(int mLineCount) {  
  15.       this.mLineCount = mLineCount;  
  16.   
  17.       invalidate();  
  18.   }  
  19.   
  20.   /** 
  21.    * 设置文本数据 
  22.    */  
  23.   public void setTextList(ArrayList<String> textList) {  
  24.       mTextList.clear();  
  25.       mTextList.addAll(textList);  
  26.   
  27.       invalidate();  
  28.   }  
  29.   
  30.   /** 
  31.    * 设置颜色数据 
  32.    * @param colorList 
  33.    */  
  34.   public void setColorList(ArrayList<Integer> colorList) {  
  35.       mColorList.clear();  
  36.       mColorList.addAll(colorList);  
  37.   
  38.       invalidate();  
  39.   }  
然后 你有没有忘记测量呢? 只要把最外面的矩形大小给他就行[java] view plaincopy手把手带你画一个漂亮蜂窝view Android自定义view手把手带你画一个漂亮蜂窝view Android自定义view
  1.     @Override  
  2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  4.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  5.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  6.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  7.   
  8.         if(widthMode == MeasureSpec.AT_MOST){  
  9.             widthSize = (int) ((3f*mColumnsCount+0.5f) *mLength);  
  10.         }else{  
  11. //            throw new IllegalStateException("only support wrap_content");  
  12.         }  
  13.   
  14.         if(heightMode == MeasureSpec.AT_MOST){  
  15.             heightSize = (int) ((mLineCount/2f +0.5f) * (Math.sqrt(3) * mLength));  
  16.         }else{  
  17.   
  18. //            throw new IllegalStateException("only support wrap_content");  
  19.         }  
  20.   
  21.   
  22.         setMeasuredDimension(widthSize,heightSize);  
  23.   
  24.   
  25.     }  


这下使用wrap_content 来看看view的大小:

手把手带你画一个漂亮蜂窝view Android自定义view

嗯。。测量也对着。。。   这里我只实现了wrap_content  大家可以以及扩展 让他支持EXACTLY




这样 一个蜂窝煤的view 就完成了。。。但是好像没鸟用的样子。。因为没有交互的话。。图片完全可以代替。所以这次就先遗留一个问题,事件的处理。其实逻辑也不是很复杂,就是判断触摸点 是否在Path内,如果action_up的时候在,分开编号,按照编号进行回调即可,这个问题,准备下篇博客解决,请大家继续关注我的博客 蟹蟹!。


本项目地址:点击打开链接


如果你觉得我写的还不错,请点击一下顶,继续关注我。谢谢!!