C语言六边形蜂巢数组,android 六边形蜂巢布局控件

Android实现蜂巢布局:从正方形到正六边形的转换
本文详细介绍了如何在Android中实现蜂巢式的布局效果,通过将复杂的六边形转换为正方形排列,然后计算各元素的位置。作者通过RecyclerView自定义LayoutManager,将视图分为两小组进行排布,并计算出相关坐标,最终实现可配置列数的正方形到正六边形的转换。文章还提到了后续将正方形转为正六边形的处理方法。

前言:最近新项目有个需求,实现蜂巢一样的布局界面。刚看到需求,心里卧槽了下,不过还挺好看的,于是思考了怎么实现。花了两三天时间,终于实现了跟我想要的差不多,封装成了比较容易拓展的使用方式。

需求效果如下:

1.蜂巢类型:

582e5d7640d478c246b8f6aea756e6bf.png

与正常图片切换效果:

7f6c851d01df494227bc5bfa7388b87c.png

6e19112e9ea9c7cc9fd54c0402031a0b.png

14daa5b152b291bedc804b8af93911a7.png

5613f3a5c4bd7129e999e00f2cf70558.png

下面是实现思路:

怎么实现呢,认真观察,我们会发现其实整个视图单个item是按照这样的规律排布六边形挨着连接,:

c84242fc307f400e9b0480b4a3ea82ec.png

所以我们的解决问题就转化为如何按照这样的规律排布。该界面复杂就复杂在一个一正六边形挨着连接,计算六边形的边以及点,显然会比较复杂,为了将问题简单化,我将正六边形转化为正方形,要知道,正六边形可以通过正方形的内切圆绘制出来:

7470fcc9a4f8d19763ec1819fa721602.png

所以这样就很简单了,上面的排布其实转化为正方形的排布了:

d3e04ad0007db05a01626f278e7cb296.png

所以我们最终实现的思路是这样的: 绘制正方形区域并按照规律排布,在正方形区域绘制出正六边形。

接下来是实现过程

实现正方形按照指定规律排布,第一时间想到的是使用RecyclerView并重写RecyclerView.LayoutManager。关于RecyclerView不懂的请百度。

如何计算正方形排布的坐标,首先,我将一组视图分成了两小组:

b8f12e52e208eabef714d22849402af3.png

为了拓展性,我们可以指定要显示的列数mColumnSize,这样我们就可以计算出第一小组最多显示个数以及第二小组最多显示个数:

int rvwidth = getRecyclerViewWidth();//RecyclerView width

int itemWidth = rvwidth / mColumnSize;//正方形宽度

int itemHeight = itemWidth;

int firstgroupnum = mColumnSize / 2 ;//第一小组最多显示个数

int secondgroupnum = mColumnSize % 2 == 0 ? mColumnSize / 2 : mColumnSize / 2 + 1;//第二组最多显示个数

这样我们可以得到了内切圆半径R:

float r = itemWidth / 2;//内切圆半径

整个组边的计算关系如下图:

24bf5c13ad4fc92ac70137881210672e.png

我们很容易就得到以下关系:

w = r

fleft = (rvwidth - (itemWidth * firstgroupnum + r * (firstgroupnum - 1))) / 2;//第一组开始偏移量

sright = fleft-itemWidth*3/4;//第二组开始偏移量

通过上面的关系我们很容易就得到了这些正方形的分布坐标。那么开始撸代码了。

为了简单易于理解,封装一下每一组的数据,每一组的数据由两小组数据组成:

private class GroupData{

int itemIndex = 0;

List FirstGroup = new ArrayList<>();

List SecondGroup = new ArrayList<>();

}

private class GroupItem{

Rect rect = null;

int itemindex = 0;

}

这样我们就可以根据第一小组显示的个数与第二小组显示的个数得到划分好的GroupData:

/** 将数据转化为组数据 *@param firstgroupnum 第一小组最多显示个数 *@param secondgroupnum 第二小组最多显示个数 *@return */

private List GetGroupData(int firstgroupnum, int secondgroupnum) {

int groupnums = getItemCount()/(firstgroupnum+secondgroupnum);

if(getItemCount()%(firstgroupnum+secondgroupnum)!=0){

groupnums++;

}

List groupdata = new ArrayList<>();

int ItemIndex = 0;

for(int index = 0;index

GroupData g = new GroupData();

for(int i=0;i

GroupItem item = new GroupItem();

item.itemindex = ItemIndex++;

item.rect = mItemFrames.get(item.itemindex);

g.FirstGroup.add(item);

}

for(int i=0;i

GroupItem item = new GroupItem();

item.itemindex = ItemIndex++;

item.rect = mItemFrames.get(item.itemindex);

g.SecondGroup.add(item);

}

groupdata.add(g);

}

return groupdata;

}

转化为组数据后,计算出上面的边关系:

float fleft = (rvwidth - (itemWidth * firstgroupnum + r * (firstgroupnum - 1))) / 2;//第一组开始偏移量

float firstgroupitemleftposition = 0;//第一组item左边位置

float sright = fleft-itemWidth*3/4;//第二组开始偏移量

float d = (float) (itemHeight / 4 * (2 - Math.sqrt(3)));//六边形到边到内切圆的距离

float secondgroupmarginfirstgroup = (float) itemHeight/2 - d;

float topmargin = 50;

float toppositoion = 0;

然后遍历排布每组的正方形,每组的正方形有两小组,分别遍历排布:

for(int index =0;index

toppositoion = index*itemHeight+topmargin+GROUP_PADDING*index;

toppositoion = toppositoion-d*2*index;

GroupData g = groupDatas.get(index);

for(int firstgroupindex =0;firstgroupindex

int left = (int) (fleft+firstgroupindex*(itemWidth+r));

int top = (int) toppositoion;

int right = left+itemWidth;

int bottom = top+itemHeight;

Rect rect = g.FirstGroup.get(firstgroupindex).rect;

rect.set(left,top,right,bottom);

}

toppositoion = toppositoion+secondgroupmarginfirstgroup+FIRSTGROUP_MARGIN_SECONDGROUP;

for(int secondgroupindex =0;secondgroupindex

int left = (int) (sright+secondgroupindex*(itemWidth+r));

int top = (int) toppositoion;

int right = left+itemWidth;

int bottom = top+itemHeight;

Rect rect = g.SecondGroup.get(secondgroupindex).rect;

rect.set(left,top,right,bottom);

}

}

注意的是,为了方便使用,我们对每组的视图增加了间距,每组的两个小组之间也增加了间距。

public int FIRSTGROUP_MARGIN_SECONDGROUP = 50;//第一小组与第二小组间距

public int GROUP_PADDING = 120;//组距

分布好后,填充视图就是了:

private void fill(RecyclerView.Recycler recycler, RecyclerView.State state) {

if (getItemCount() <= 0 || state.isPreLayout()) {

return;

}

Rect displayRect = new Rect(mHorizontalOffset, mVerticalOffset,

getHorizontalSpace() + mHorizontalOffset,

getVerticalSpace() + mVerticalOffset);

for (int i = 0; i < getItemCount(); i++) {

Rect frame = mItemFrames.get(i);

if (Rect.intersects(displayRect, frame)) {

View scrap = recycler.getViewForPosition(i);

addView(scrap);

measureChildWithMargins(scrap, 0, 0);

layoutDecorated(scrap, frame.left - mHorizontalOffset, frame.top - mVerticalOffset,

frame.right - mHorizontalOffset, frame.bottom - mVerticalOffset);

}

}

}

完成到这里后,我们就可以实现到这样的效果了:

间距为50显示5列的效果:

f7a7415d360bb22e740345cde5459f6e.png

间距为50显示7列的效果:

1262a3fed142c241fa28db33414b0917.png

剩下的最后一步,就是将正方形图片转化为正六边形而已,具体实现代码,参考我这篇文章:android六边形imageview

最终转化后得到效果如下:

829418bc54a7f0f2b08f77e9d88053ce.png

5eeed2e6ea44c558153763889dcb43e5.png

9c52375a2adf0214802e16833d34e0e5.png

3dafd8e7f40238d4551e25e04e56c025.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值