freeseawind.ninepatch.common.AbstractNinePatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of NinePatch Show documentation
Show all versions of NinePatch Show documentation
Swing and Javafx ninepatch
The newest version!
package freeseawind.ninepatch.common;
import java.awt.Rectangle;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import freeseawind.ninepatch.common.Row.Type;
/**
* @author freeseawind@github
*
*/
public abstract class AbstractNinePatch
{
/**
* 拉伸区域,像素值必须为黑色 0xFF000000
*/
public static final int PATCH_PIXES = 0xFF000000;
private int lastWidth;
private int lastHeight;
private int patchWidth;
private int patchHeight;
private int horizontalPatchNum;
private int verticalPatchNum;
private List> columns;
private Padding padding;
private T image;
private RepeatType repeatType;
public AbstractNinePatch(T image)
{
this(image, null);
}
public AbstractNinePatch(T image, RepeatType repeatType)
{
image = toCompatibleImage(image);
countPatch(image);
this.image = image;
this.repeatType = repeatType;
}
/**
*
* @param g2d
* @param x
* @param y
* @param scaledWidth
* @param scaledHeight
*/
public void drawNinePatch(E g2d, int x, int y, int scaledWidth, int scaledHeight)
{
// 修复BUG防止拉伸大小小于等于原图大小
if (scaledWidth <= 1 || scaledHeight <= 1)
{
return;
}
try
{
if(lastWidth != scaledWidth || lastHeight != scaledHeight)
{
lastWidth = scaledWidth;
lastHeight = scaledHeight;
resetData(scaledWidth, scaledHeight);
}
if(patchWidth == scaledWidth && patchHeight == scaledHeight)
{
drawImage(g2d, image, x, y, scaledWidth, scaledHeight);
return;
}
translate(g2d, x, y);
int startX = 0;
int startY = 0;
int minWidth = patchWidth;
int minHeight = patchHeight;
if(horizontalPatchNum > 1)
{
minWidth = (patchWidth / horizontalPatchNum);
}
if(verticalPatchNum > 1)
{
minHeight = (patchHeight / verticalPatchNum);
}
int columnCount = 0;
// 逐行绘制
for(List rows : columns)
{
int rowCount = 0;
int height = patchHeight;
boolean isFirst = true;
int preRowHeight = 0;
// 防止图片拉伸高度大于实际需要拉伸高度
if(startY >= scaledHeight)
{
break;
}
for(Row row : rows)
{
Rectangle rect = row.getRectangle();
int width = rect.width;
// 防止图片拉伸宽度大于实际需要拉伸宽度
if(startX >= scaledWidth)
{
break;
}
if(Type.HORIZONTALPATCH == row.getType() || Type.TILEPATCH == row.getType())
{
// 计算拉伸的宽度
width = (patchWidth - minWidth * (rowCount + 1));
if(width < minWidth)
{
width = patchWidth - (minWidth * rowCount);
}
else
{
width = minWidth;
}
rowCount++;
}
else if(Type.HORIZONTALPATCH == row.getType())
{
// 计算拉伸的高度
if(isFirst)
{
height = (patchHeight - minHeight * (columnCount + 1));
if(height < minHeight)
{
height = patchHeight - (minHeight * columnCount);
}
else
{
height = minHeight;
}
columnCount++;
isFirst = false;
}
}
// 绘制固定区域
if(Type.FIX == row.getType())
{
drawImage(g2d, image, rect.x, rect.y, rect.width, rect.height, startX, startY, rect.width, rect.height);
startX += rect.width;
preRowHeight = rect.height;
}
else if(Type.HORIZONTALPATCH == row.getType())
{
// 绘制水平拉伸区域
drawImage(g2d, image, rect.x, rect.y, rect.width, rect.height, startX, startY, width, rect.height);
startX += width;
preRowHeight = rect.height;
}
else if(Type.VERTICALPATCH == row.getType())
{
// 垂直拉伸
drawImage(g2d, image, rect.x, rect.y, rect.width, rect.height, startX, startY, rect.width, height);
startX += rect.width;
preRowHeight = height;
}
else if(Type.TILEPATCH == row.getType())
{
// 平铺
if(repeatType != null)
{
repeatImage(g2d, image, rect.x, rect.y, rect.width, rect.height, startX, startY, width, height);
}
else
{
drawImage(g2d, image, rect.x, rect.y, rect.width, rect.height, startX, startY, width, height);
}
startX += width;
preRowHeight = height;
}
}
startX = 0;
startY += preRowHeight;
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
translate(g2d, -x, -y);
}
}
public List> countColumn(NinePatchRegion xRegions, NinePatchRegion yRegions)
{
boolean isPatchY = false; // 当前是否处于拉伸区域
int i = 0; // 固定区域起始索引
int j = 0;// 拉伸区域起始索引
int patchNum = yRegions.getPatchRegions().size();
int fixNum = yRegions.getFixRegions().size();
Region yRegion = null; // 循环出口条件
List> columns = new LinkedList>();// 九宫格行集合
do
{
yRegion = null;
if (isPatchY && patchNum >= j + 1)
{
yRegion = yRegions.getPatchRegions().get(j++);
}
if (!isPatchY && fixNum >= i + 1)
{
yRegion = yRegions.getFixRegions().get(i++);
}
if(yRegion != null)
{
columns.add(countRow(yRegion, xRegions, isPatchY));
}
isPatchY = !isPatchY;
}
while (yRegion != null);
return columns;
}
/**
* 计算点九图的每一列区域
* @param yRegion
* @param xRegions
* @param isPatchY
* @return 返回当前行
*/
public List countRow(Region yRegion, NinePatchRegion xRegions, boolean isPatchY)
{
boolean isPatchX = false;
int i = 0;
int j = 0;
int patchNum = xRegions.getPatchRegions().size();
int fixNum = xRegions.getFixRegions().size();
Region xRegion = null;
List column = new LinkedList();
do
{
xRegion = null;
if (isPatchX && patchNum >= j + 1)
{
xRegion = xRegions.getPatchRegions().get(j++);
}
if (!isPatchX && fixNum >= i + 1)
{
xRegion = xRegions.getFixRegions().get(i++);
}
if(xRegion != null)
{
Row.Type rowType = getRowType(isPatchX, isPatchY);
int height = yRegion.getEnd() - yRegion.getStart();
int width = xRegion.getEnd() - xRegion.getStart();
Rectangle rect = new Rectangle(xRegion.getStart() + 1, yRegion.getStart() + 1, width, height);
Row row = new Row(rect, rowType);
column.add(row);
}
isPatchX = !isPatchX;
}
while (xRegion != null);
Collections.sort(column);
return column;
}
/**
* 获取内容显示区域的间距
* @param w 内容面板的宽度
* @param h 内容面板的高度
* @param xRegions x坐标集合
* @param yRegions y坐标集合
* @return
*/
public Padding getPadding(int w, int h, List xRegions, List yRegions)
{
Region xRegion = xRegions.get(0);
Region yRegion = yRegions.get(0);
int left = xRegion.getStart();
int top = yRegion.getStart();
int right = w - xRegion.getEnd();
int bottom = h - yRegion.getEnd();
return new Padding(left, top, right, bottom);
}
/**
* 根据像素值集合计算当前像素区域中的固定区域和拉伸区域
* @param pixels 需要查找的像素集合
* @return
*/
public NinePatchRegion getPatches(int[] pixels)
{
int start = 0;
int lastPixel = pixels[0];
List fixArea = new LinkedList();
List patchArea = new LinkedList();
for(int i = 1; i <= pixels.length; i++)
{
if(i < pixels.length && lastPixel == pixels[i])
{
continue;
}
// 区间对象
Region region = new Region(start, i);
if (PATCH_PIXES == lastPixel)
{
patchArea.add(region);
}
else
{
fixArea.add(region);
}
start = i;
if(i < pixels.length)
{
lastPixel = pixels[i];
}
}
// 像素集合中没有找到特殊像素
if(start == 0)
{
Region region = new Region(start, pixels.length);
if (PATCH_PIXES == lastPixel)
{
patchArea.add(region);
}
else
{
fixArea.add(region);
}
}
return new NinePatchRegion(fixArea, patchArea);
}
/**
* 平铺图片
* @param g2d
* @param image
* @param x
* @param y
* @param sw
* @param sh
* @param dx
* @param dy
* @param dw
* @param dh
*/
public void repeatImage(E g2d,
T image,
int x,
int y,
int sw,
int sh,
int dx,
int dy,
int dw,
int dh)
{
if (repeatType == null)
{
return;
}
if (repeatType == RepeatType.HORIZONTAL)
{
int hornaizeW = dw;
//
do
{
if (hornaizeW - sw < 0)
{
sw = hornaizeW;
}
hornaizeW -= sw;
drawImage(g2d, image, x, y, sw, sh, dx, dy, sw, dh);
dx += sw;
}
while (hornaizeW > 0);
}
else if (repeatType == RepeatType.VERTICAL)
{
int verticalH = dh;
// ˮƽÀÉì
do
{
if (verticalH - sh < 0)
{
sh = verticalH;
}
verticalH -= sh;
drawImage(g2d, image, x, y, sw, sh, dx, dy, dw, sh);
dy += sh;
}
while (verticalH > 0);
}
}
/**
* 计算点九图片信息
* |1| 2 |3|
* |4| 5 |6|
* |7| 8 |9|
* 计算图片中每一行里的固定区域、垂直拉伸区、水平拉伸区和平铺区域以及内容显示间距
* @param image
*/
protected void countPatch(T image)
{
// 图片实际宽度, 不包含左右控制区域
int width = getImageWidth(image) - 2;
// 图片实际高度, 不包含上下控制区域
int height = getImageHeight(image) - 2;
// 行
int[] row = null;
// 列
int[] column = null;
// 获取左侧拉伸区域的像素
column = getPixels(image, 0, 1, 1, height);
// 获取左侧垂直列
NinePatchRegion left = getPatches(column);
// 获取顶部拉伸区域的像素值, 不包含左右控制区域,所以向右偏移一个像素
row = getPixels(image, 1, 0, width, 1);
// 获取顶部水平行
NinePatchRegion top = getPatches(row);
// 水平拉伸区域素数量
this.horizontalPatchNum = top.getPatchRegions().size();
// 垂直拉伸区域数量
this.verticalPatchNum = left.getPatchRegions().size();
this.columns = countColumn(top, left);
// 获取底部水平内容拉伸区域像素集合
row = getPixels(image, 1, height + 1, width, 1);
// 获取右侧垂直内容拉伸区域像素集合
column = getPixels(image, width + 1, 1, 1, height);
NinePatchRegion bottom = getPatches(row);
NinePatchRegion right = getPatches(column);
this.padding = getPadding(width, height, bottom.getPatchRegions(), right.getPatchRegions());
}
protected T toCompatibleImage(T image)
{
return image;
}
/**
* 从指定的矩形区域中读取像素数据
* @param img
* @param x 起始x坐标
* @param y 起始y坐标
* @param w 矩形的宽度
* @param h 矩形的高度
* @return 指定矩形区域像素数据整型数组
*/
public abstract int[] getPixels(T img, int x, int y, int w, int h);
/**
* 获取图片宽度
* @param img
* @return
*/
public abstract int getImageWidth(T img);
/**
* 获取图片高度
* @param img
* @return
*/
public abstract int getImageHeight(T img);
/**
*
* @param g2d
* @param x
* @param y
*/
public abstract void translate(E g2d, int x, int y);
/**
*
* @param g2d
* @param image
* @param x
* @param y
* @param scaledWidth
* @param scaledHeight
*/
public abstract void drawImage(E g2d,
T image,
int x,
int y,
int scaledWidth,
int scaledHeight);
/**
*
* @param g2d
* @param image
* @param sx
* @param sy
* @param sw
* @param sh
* @param dx
* @param dy
* @param dw
* @param dh
*/
public abstract void drawImage(E g2d,
T image,
int sx,
int sy,
int sw,
int sh,
int dx,
int dy,
int dw,
int dh);
/**
*
* @param scaleWidth
* @param scaleHeight
*/
private void resetData(int scaleWidth, int scaleHeight)
{
this.patchWidth = scaleWidth;
this.patchHeight = scaleHeight;
boolean isFirst = true;
boolean isNewColumn = true;
for(List rows : columns)
{
for(Row row : rows)
{
if(Type.FIX == row.getType() && isFirst)
{
patchWidth -= row.getRectangle().width;
}
//BUG FIX: 修复显示区域计算问题
if(Type.FIX == row.getType() && isNewColumn)
{
patchHeight -= row.getRectangle().height;
isNewColumn = false;
}
}
isNewColumn = true;
isFirst = false;
}
}
private Type getRowType(boolean isPatchX, boolean isPatchY)
{
if (!isPatchX && !isPatchY)
{
return Type.FIX;
}
if (!isPatchX && isPatchY)
{
return Type.VERTICALPATCH;
}
if (isPatchX && !isPatchY)
{
return Type.HORIZONTALPATCH;
}
return Type.TILEPATCH;
}
public Padding getPadding()
{
return padding;
}
}