由于本文的重点不在于涂鸦板,所以就很随便了,哈哈

先发几张效果图:

这个工具其实是帮一位群友写的,写完感觉效果还可以,所以就发出来,希望能帮到更多的人。

先说说这个工具的原理:
先获取两张图片中,所含有画笔颜色的像素点;
将手绘图的像素点映射表 (int 二维数组,0是无,1是有画笔颜色),跟原图的画笔颜色像素点匹配,得出两张图片的差异,可以得到的数据有:
原图对应颜色像素点数;(现在即黑色)
手绘图对应颜色像素点数;(现在即红色)
两图完全重合的像素点数;
两图相近的像素点数;(指定抖动距离之内的,比如现在是5像素)
两图不匹配的像素点数;(抖动距离以外的)
自己计算的相似度;(哈哈,这个仅代表本人的意见)

我们来看看怎样判断该像素色值是否近似画笔颜色:

    private static boolean isLookLikePaintColor(int r, int g, int b) {for (int[] color : mPaintColors) {int dither = 10; //允许色值的抖动范围是10 (max = 255)int redDither = Math.abs(color[0] - r);int greenDither = Math.abs(color[1] - g);int blueDither = Math.abs(color[2] - b);if (redDither < dither && greenDither < dither && blueDither < dither)return true;}return false;}

拿到像素的rgb色值后,将色值跟指定画笔色值的差 的绝对值 < 10,那么我们就认为这个颜色近似。

那我们怎样拿到bitmap的像素点的rgb色值呢?

哈哈,Color类已经有提供方法给我们了:

int pixelColor = bitmap.getPixel(x, y);
int r = Color.red(pixelColor);
int g = Color.green(pixelColor);
int b = Color.blue(pixelColor);

其实用bitmap.getPixel(x, y)方法拿到色值,这样一张图片下来,成千上万个像素点,效率就有点低了,幸好bitmap类还有个getPixels方法,这个方法可以一次拿到一张图片的全部色值。

下面封装了一个获取像素色值的方法:

    /*** 获取目标bitmap的所有像素色值** @param target  目标bitmap* @param handler 回调接口*/private static void getBitmapPixelColor(Bitmap target, PixelColorHandler handler) {if (checkBitmapCanUse(target) && handler != null) {int width = target.getWidth(), height = target.getHeight();int[] targetPixels = new int[width * height];//获取bitmap所有像素点target.getPixels(targetPixels, 0, width, 0, 0, width, height);int index = 0;int pixelColor;int r, g, b;for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {pixelColor = targetPixels[index];r = Color.red(pixelColor);g = Color.green(pixelColor);b = Color.blue(pixelColor);handler.onHandle(target, index, x, y, r, g, b);++index;}}}}/*** 处理像素点的回调接口*/private interface PixelColorHandler {void onHandle(Bitmap target, int index, int x, int y, int r, int g, int b);}

下面就到了一个相对复杂点的东西了,检查周围 n 个像素点内的色值是否与画笔颜色匹配:
需要用到一个 "广度优先遍历"  ,下面我们来看看怎样实现:
由于代码里面注释比较详细,这里就不写了。

    /*** 以广度优先的遍历方法,以currentPos为起点,来查找相近nearCount个像素点的色值是否近似于目标颜色{@link BitmapCompareUtil.mPaintColors}** @param nearCount    需要查找周边像素的个数* @param targetPixels 色值表* @param currentPos   当前坐标* @return 是否查找到*/private static boolean checkNearItemIsHit(int nearCount, int[][] targetPixels, Point currentPos) {//先初始化数据int length = nearCount * 2 + 1;int[][] state = new int[length][length];int verticalCount = targetPixels.length;int horizontalCount = targetPixels[0].length;Queue<Point> queue = new ArrayDeque<>(); //这个是应该查找的点的队列queue.offer(new Point(nearCount, nearCount)); //当前pos先入队state[nearCount][nearCount] = 1; //标记该点无效(已经被用过)while (!queue.isEmpty()) { //有任务未完成Point header = queue.poll(); //队头出队List<Point> directions = getCanArrivePos(state, header); //获取队头周边8个可到达的点 (注意是可到达,不包括已经走过的点)//遍历获取到的周边点for (int i = 0; i < directions.size(); i++) {Point direction = directions.get(i);//调整坐标int x = direction.x < length ? currentPos.x - direction.x : currentPos.x + direction.x;int y = direction.y < length ? currentPos.y - direction.y : currentPos.y + direction.y;//检查越界if (!isOutside(direction.x, direction.y, verticalCount, horizontalCount)&& !isOutside(x, y, verticalCount, horizontalCount)) {//等于1表示查找到了匹配的点if (targetPixels[y][x] == 1)return true;//否则将这个点入队,需要查找这个点的周围elsequeue.offer(direction);}}}//任务队列为空,且还没返回,自然是找不到了return false;}/*** 根据当前点获取周围8个相邻的点,如下:* 1 2 3* 4 * 5* 6 7 8** @param state      状态映射表* @param currentPos 当前坐标点* @return 可以到达的像素点 (未被标记获取过的)*/private static List<Point> getCanArrivePos(int[][] state, Point currentPos) {List<Point> result = new ArrayList<>();for (int i = 0; i < 8; i++) {Point tmp = getNextPosition(currentPos, i);//检查下一个点是否越界if ((tmp.x > -1 && tmp.x < state.length) && (tmp.y > -1 && tmp.y < state.length))//检查这个点是否已经被标记过无效if (state[tmp.y][tmp.x] != 1) {result.add(tmp);//标记该点无效state[tmp.y][tmp.x] = 1;}}return result;}/*** 根据当前点和方向获取下一个方向的点** @param currentPos 当前坐标点* @param direction  要获取的方向* @return 对应的点*/private static Point getNextPosition(Point currentPos, int direction) {Point result = new Point(currentPos.x, currentPos.y);switch (direction) {//左case 0:result.x -= 1;break;//上case 1:result.y -= 1;break;//下case 2:result.y += 1;break;//右case 3:result.x += 1;break;//左上case 4:result.x -= 1;result.y -= 1;break;//左下case 5:result.x -= 1;result.y += 1;break;//右上case 6:result.x += 1;result.y -= 1;break;//右下case 7:result.x += 1;result.y += 1;break;}return result;}/*** 检查目标pos是否越界*/private static boolean isOutside(int x, int y, int verticalCount, int horizontalCount) {return x < 0 || y < 0 || x > horizontalCount - 1 || y > verticalCount - 1;}

现在就剩下主方法了:
由于代码里面注释比较详细,这里就不写了。

    /*** 获取两张图片的数据比较** @param src         原图* @param target      要比较的图片* @param paintColors 需要被检测到的颜色* @return 两图的像素信息*/public static DiffInfo getBitmapDiffInfo(Bitmap src, Bitmap target, int... paintColors) {//先将原图缩放4倍 (提高执行效率,因为是涂鸦线条,基本不会影响到最终的结果的)float scale = .25F;Bitmap srcTemp = scaleBitmap(src, (int) (src.getWidth() * scale), (int) (src.getHeight() * scale));Bitmap targetTemp = scaleBitmap(target, (int) (target.getWidth() * scale), (int) (target.getHeight() * scale));//初始化需要被检测到的颜色initPaintColors(paintColors);int srcBitmapDarkPixelCount, //原图对应颜色像素点数targetBitmapDarkPixelCount, //目标图对应颜色像素点数hitCount, //完全重合的像素点数nearCount, //相近的像素点数awayCount; //不匹配的像素点数float similarityDegree; //自己计算的相似度hitCount = nearCount = awayCount = 0;List<Point> targetBitmapPixelPoints = new ArrayList<>(); //目标bitmap像素点//初始化目标bitmaptargetBitmapDarkPixelCount = getLookLikePaintColorPixel(targetTemp, 0, 0, targetBitmapPixelPoints, null);int[][] srcBitmapPixelPointsTable = new int[srcTemp.getHeight()][srcTemp.getWidth()]; //原图像素点映射表//初始化原图srcBitmapDarkPixelCount = getLookLikePaintColorPixel(srcTemp, 0, 0, null, srcBitmapPixelPointsTable);//遍历目标bitmap获取的像素点,并查找匹配for (Point point : targetBitmapPixelPoints) {//检查数组越界 (因为目标bitmap可能比原图大)if (!isOutside(point.x, point.y, srcBitmapPixelPointsTable.length, srcBitmapPixelPointsTable[0].length)) {if (srcBitmapPixelPointsTable[point.y][point.x] == 1) {//等于1,则完全匹配hitCount++;} else {//检查周边5个像素点是否有匹配到if (checkNearItemIsHit(5, srcBitmapPixelPointsTable, point))nearCount++;else awayCount++;}}}//(自己计算的)相似度的基本分,根据两图获取的像素的相差百分比similarityDegree = (float) targetBitmapDarkPixelCount / (float) srcBitmapDarkPixelCount;if (similarityDegree > 1) {//目标bitmap的像素点比原图的多,则用1减去多出来的float more = Math.abs(1 - similarityDegree);similarityDegree = 1 - more;}//计算两图完全匹配的像素点与总像素点的百分比float hitPercent = (float) hitCount / (float) srcBitmapDarkPixelCount;//计算相邻的像素点与总像素点的百分比float nearPercent = (float) nearCount / (float) srcBitmapDarkPixelCount;//这个我们把2个相邻的点,当作一个完全匹配的点hitPercent += nearPercent / 2;//再用基本分 - 1 - 优化后的完全命中百分比similarityDegree -= 1 - hitPercent;if (similarityDegree < 0)similarityDegree = 0;//因为刚刚缩放了图片,所以这些数据也要恢复 (除以缩放比例)return new DiffInfo((int) (srcBitmapDarkPixelCount / scale), (int) (targetBitmapDarkPixelCount / scale),(int) (hitCount / scale), (int) (nearCount / scale), (int) (awayCount / scale), similarityDegree);}

有一点值得注意的就是:
由于涂鸦板和原图是分离的,涂鸦板是另外一个canvas对象,所以现在看到的白色背景是原图的白色背景,
涂鸦板其实是透明的,
所以在比较两张图片的时候,如果原图的画笔颜色是黑色,一定要在涂鸦板上画上一张其他颜色的背景,不然会检测不到颜色的。像这样:

        mCanvas.drawColor(Color.WHITE);drawHistory();BitmapCompareUtil.DiffInfo diffInfo = BitmapCompareUtil.getBitmapDiffInfo(mBackground, mHistoryBitmap, 0, 0, Color.BLACK, Color.RED);mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);drawHistory();

为什么呢?
因为上面获取颜色的时候,为了效率,就没有拿到alpha的色值,所以默认的透明rgb色值是0,0,0   和黑色的rgb色值是一样的!导致isLookLikePaintColor这个方法误认为这个透明颜色是黑色,所以就返回true了,
如果不想在比较前画白色背景,可以在初始化画笔颜色的方法和判断色值的方法 加上 alpha色值就可以了:

    /*** 根据色值判断和目标颜色是否近似*/private static boolean isLookLikePaintColor(int a, int r, int g, int b) {for (int[] color : mPaintColors) {int dither = 10; //允许色值的抖动范围是10 (max = 255)int alphaDither = Math.abs(color[0] - a);int redDither = Math.abs(color[1] - r);int greenDither = Math.abs(color[2] - g);int blueDither = Math.abs(color[3] - b);if (alphaDither < dither && redDither < dither && greenDither < dither && blueDither < dither)return true;}return false;}/*** 初始化 {@link  BitmapCompareUtil.mPaintColors}*/private static void initPaintColors(int[] paintColors) {mPaintColors = new int[paintColors.length][4];for (int index = 0; index < paintColors.length; index++) {int a, r, g, b;int color = paintColors[index];a = Color.alpha(color);r = Color.red(color);g = Color.green(color);b = Color.blue(color);mPaintColors[index][0] = a;mPaintColors[index][1] = r;mPaintColors[index][2] = g;mPaintColors[index][3] = b;}}

这样得到的默认透明色值就是0,0,0,0  黑色就是255,0,0,0。哈哈

下面发一下封装后的工具类:


/*** Created by wuyr on 17-12-12 下午4:20.*/@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
public class BitmapCompareUtil {/*** 需要被检测到的颜色*/private static int[][] mPaintColors;/*** 获取两张图片的数据比较** @param src         原图* @param target      要比较的图片* @param paintColors 需要被检测到的颜色* @return 两图的像素信息*/public static DiffInfo getBitmapDiffInfo(Bitmap src, Bitmap target, int... paintColors) {//先将原图缩放4倍 (提高执行效率,因为是涂鸦线条,基本不会影响到最终的结果的)float scale = .25F;Bitmap srcTemp = scaleBitmap(src, (int) (src.getWidth() * scale), (int) (src.getHeight() * scale));Bitmap targetTemp = scaleBitmap(target, (int) (target.getWidth() * scale), (int) (target.getHeight() * scale));//初始化需要被检测到的颜色initPaintColors(paintColors);int srcBitmapDarkPixelCount, //原图对应颜色像素点数targetBitmapDarkPixelCount, //目标图对应颜色像素点数hitCount, //完全重合的像素点数nearCount, //相近的像素点数awayCount; //不匹配的像素点数float similarityDegree; //自己计算的相似度hitCount = nearCount = awayCount = 0;List<Point> targetBitmapPixelPoints = new ArrayList<>(); //目标bitmap像素点//初始化目标bitmaptargetBitmapDarkPixelCount = getLookLikePaintColorPixel(targetTemp, 0, 0, targetBitmapPixelPoints, null);int[][] srcBitmapPixelPointsTable = new int[srcTemp.getHeight()][srcTemp.getWidth()]; //原图像素点映射表//初始化原图srcBitmapDarkPixelCount = getLookLikePaintColorPixel(srcTemp, 0, 0, null, srcBitmapPixelPointsTable);//遍历目标bitmap获取的像素点,并查找匹配for (Point point : targetBitmapPixelPoints) {//检查数组越界 (因为目标bitmap可能比原图大)if (!isOutside(point.x, point.y, srcBitmapPixelPointsTable.length, srcBitmapPixelPointsTable[0].length)) {if (srcBitmapPixelPointsTable[point.y][point.x] == 1) {//等于1,则完全匹配hitCount++;} else {//检查周边5个像素点是否有匹配到if (checkNearItemIsHit(5, srcBitmapPixelPointsTable, point))nearCount++;else awayCount++;}}}//(自己计算的)相似度的基本分,根据两图获取的像素的相差百分比similarityDegree = (float) targetBitmapDarkPixelCount / (float) srcBitmapDarkPixelCount;if (similarityDegree > 1) {//目标bitmap的像素点比原图的多,则用1减去多出来的float more = Math.abs(1 - similarityDegree);similarityDegree = 1 - more;}//计算两图完全匹配的像素点与总像素点的百分比float hitPercent = (float) hitCount / (float) srcBitmapDarkPixelCount;//计算相邻的像素点与总像素点的百分比float nearPercent = (float) nearCount / (float) srcBitmapDarkPixelCount;//这个我们把2个相邻的点,当作一个完全匹配的点hitPercent += nearPercent / 2;//再用基本分 - 1 - 优化后的完全命中百分比similarityDegree -= 1 - hitPercent;if (similarityDegree < 0)similarityDegree = 0;//因为刚刚缩放了图片,所以这些数据也要恢复 (除以缩放比例)return new DiffInfo((int) (srcBitmapDarkPixelCount / scale), (int) (targetBitmapDarkPixelCount / scale),(int) (hitCount / scale), (int) (nearCount / scale), (int) (awayCount / scale), similarityDegree);}/*** 获取目标bitmap近似画笔颜色的像素点** @param target 目标bitmap* @param startX bitmap在canvas中的x轴* @param startY bitmap在canvas中的y轴* @param data   获取到的像素点 以List的形式* @param table  获取到的像素点 以二位数组的形式* @return 获取到的像素点数*/private static int getLookLikePaintColorPixel(Bitmap target, final int startX, final int startY, final List<Point> data, final int[][] table) {final int[] darkColorCount = {0};getBitmapPixelColor(target, new PixelColorHandler() {@Overridepublic void onHandle(Bitmap target, int index, int x, int y, int r, int g, int b) {if (table != null)table[y][x] = 1;if (data != null)data.add(new Point(startX + x, startY + y));++darkColorCount[0];}});return darkColorCount[0];}/*** 获取目标bitmap近似画笔颜色的像素点** @param target  目标bitmap* @param handler 回调接口*/private static void getBitmapPixelColor(Bitmap target, PixelColorHandler handler) {if (checkBitmapCanUse(target) && handler != null) {int width = target.getWidth(), height = target.getHeight();int[] targetPixels = new int[width * height];//获取bitmap所有像素点target.getPixels(targetPixels, 0, width, 0, 0, width, height);int index = 0;int pixelColor;int r, g, b;for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {//获取rgb色值并与目标颜色相比较pixelColor = targetPixels[index];r = Color.red(pixelColor);g = Color.green(pixelColor);b = Color.blue(pixelColor);if (isLookLikePaintColor(r, g, b))handler.onHandle(target, index, x, y, r, g, b);++index;}}}}/*** 将目标bitmap进行缩放** @param target 目标bitmap* @param w      新的宽度* @param h      新的高度* @return 缩放后的bitmap*/public static Bitmap scaleBitmap(Bitmap target, int w, int h) {if (target == null || target.isRecycled()) return target;int width = target.getWidth(), height = target.getHeight();Matrix matrix = new Matrix();matrix.postScale(((float) w / width), ((float) h / height));return Bitmap.createBitmap(target, 0, 0, width, height, matrix, true);}/*** 检查目标bitmap是否可用*/private static boolean checkBitmapCanUse(Bitmap target) {return target != null && !target.isRecycled();}/*** 根据色值判断和目标颜色是否近似*/private static boolean isLookLikePaintColor(int r, int g, int b) {for (int[] color : mPaintColors) {int dither = 10; //允许色值的抖动范围是10 (max = 255)int redDither = Math.abs(color[0] - r);int greenDither = Math.abs(color[1] - g);int blueDither = Math.abs(color[2] - b);if (redDither < dither && greenDither < dither && blueDither < dither)return true;}return false;}/*** 初始化 {@link  BitmapCompareUtil.mPaintColors}*/private static void initPaintColors(int[] paintColors) {mPaintColors = new int[paintColors.length][3];for (int index = 0; index < paintColors.length; index++) {int r, g, b;int color = paintColors[index];r = Color.red(color);g = Color.green(color);b = Color.blue(color);mPaintColors[index][0] = r;mPaintColors[index][1] = g;mPaintColors[index][2] = b;}}/*** 以广度优先的遍历方法,以currentPos为起点,来查找相近nearCount个像素点的色值是否近似于目标颜色{@link BitmapCompareUtil.mPaintColors}** @param nearCount    需要查找周边像素的个数* @param targetPixels 色值表* @param currentPos   当前坐标* @return 是否查找到*/private static boolean checkNearItemIsHit(int nearCount, int[][] targetPixels, Point currentPos) {//先初始化数据int length = nearCount * 2 + 1;int[][] state = new int[length][length];int verticalCount = targetPixels.length;int horizontalCount = targetPixels[0].length;Queue<Point> queue = new ArrayDeque<>(); //这个是应该查找的点的队列queue.offer(new Point(nearCount, nearCount)); //当前pos先入队state[nearCount][nearCount] = 1; //标记该点无效(已经被用过)while (!queue.isEmpty()) { //有任务未完成Point header = queue.poll(); //队头出队List<Point> directions = getCanArrivePos(state, header); //获取队头周边8个可到达的点 (注意是可到达,不包括已经走过的点)//遍历获取到的周边点for (int i = 0; i < directions.size(); i++) {Point direction = directions.get(i);//调整坐标int x = direction.x < length ? currentPos.x - direction.x : currentPos.x + direction.x;int y = direction.y < length ? currentPos.y - direction.y : currentPos.y + direction.y;//检查越界if (!isOutside(direction.x, direction.y, verticalCount, horizontalCount)&& !isOutside(x, y, verticalCount, horizontalCount)) {//等于1表示查找到了匹配的点if (targetPixels[y][x] == 1)return true;//否则将这个点入队,需要查找这个点的周围elsequeue.offer(direction);}}}//任务队列为空,且还没返回,自然是找不到了return false;}/*** 根据当前点获取周围8个相邻的点,如下:* 1 2 3* 4 * 5* 6 7 8** @param state      状态映射表* @param currentPos 当前坐标点* @return 可以到达的像素点 (未被标记获取过的)*/private static List<Point> getCanArrivePos(int[][] state, Point currentPos) {List<Point> result = new ArrayList<>();for (int i = 0; i < 8; i++) {Point tmp = getNextPosition(currentPos, i);//检查下一个点是否越界if ((tmp.x > -1 && tmp.x < state.length) && (tmp.y > -1 && tmp.y < state.length))//检查这个点是否已经被标记过无效if (state[tmp.y][tmp.x] != 1) {result.add(tmp);//标记该点无效state[tmp.y][tmp.x] = 1;}}return result;}/*** 根据当前点和方向获取下一个方向的点** @param currentPos 当前坐标点* @param direction  要获取的方向* @return 对应的点*/private static Point getNextPosition(Point currentPos, int direction) {Point result = new Point(currentPos.x, currentPos.y);switch (direction) {//左case 0:result.x -= 1;break;//上case 1:result.y -= 1;break;//下case 2:result.y += 1;break;//右case 3:result.x += 1;break;//左上case 4:result.x -= 1;result.y -= 1;break;//左下case 5:result.x -= 1;result.y += 1;break;//右上case 6:result.x += 1;result.y -= 1;break;//右下case 7:result.x += 1;result.y += 1;break;}return result;}/*** 检查目标pos是否越界*/private static boolean isOutside(int x, int y, int verticalCount, int horizontalCount) {return x < 0 || y < 0 || x > horizontalCount - 1 || y > verticalCount - 1;}/*** 处理像素点的回调接口*/private interface PixelColorHandler {void onHandle(Bitmap target, int index, int x, int y, int r, int g, int b);}public static class DiffInfo {public int srcPixelCount, //原图对应颜色像素点数targetPixelCount,//目标图对应颜色像素点数hitCount, //完全重合的像素点数nearCount, //相近的像素点数awayCount; //不匹配的像素点数public float similarityDegree; //自己计算的相似度public DiffInfo(int srcPixelCount, int targetPixelCount, int hitCount, int nearCount, int awayCount, float similarityDegree) {this.srcPixelCount = srcPixelCount;this.targetPixelCount = targetPixelCount;this.hitCount = hitCount;this.nearCount = nearCount;this.awayCount = awayCount;this.similarityDegree = similarityDegree;}@Overridepublic String toString() {return String.format(Locale.getDefault(),"similarityDegree: %s\tsrcPixelCount: %s\ttargetPixelCount: %s\thitCount: %s\tnearCount: %s\tawayCount: %s",similarityDegree, srcPixelCount, targetPixelCount, hitCount, nearCount, awayCount);}}
}

本文到此结束,有错误的地方请指出,谢谢大家!

Android简单实现比较两张涂鸦相似度相关推荐

  1. android制作9图片工具,Android设计必备神器——两张图教会你怎么快速制作点九图(.9.pn...

    ndroid智能手机大流行,好多设计师都与Android开发打交道,其中点九图是个绕不开的坎儿! NinePng九图神器,是一款专门处理点九图的工具,能够帮助设计师方便快捷的处理点九图,并且非常方便的 ...

  2. Photoshop的简单使用——将两张.jpg图片拼接到一张图上

    1.准备好图片A<你想要p上去的那张照片>和图片B<你要的背景图片>,打开Photoshop,打开图片A. 2.利用裁剪工具,将所要其中一张图片进行裁剪 3.裁剪后的图片,保存 ...

  3. Android 应用性能优化(5)---用两张图告诉你,为什么你的App会卡顿?

    用两张图告诉你,为什么你的App会卡顿? Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? 知道Android究竟是如何在屏幕上显示我们期望的 ...

  4. 翻翻git之---一个简单的标签控件 LabelView (随手发了两张小宝宝的玩耍照)

    转载请注明出处:王亟亟的大牛之路 P1:这部分为废话技术内容在P2,不想看的可跳过 最近每天都是在照顾鱼,麦麦,当当之间游离回家几乎没学习,没敲代码,或者说没开工作电脑,慢慢的罪恶感,贴两张周末小朋友 ...

  5. android接口回调的两中简单写法

    android接口回调的两中简单写法--B509小贴士 一.第一种内部重写(较简单,常用异步) (1)创建接口 ,并实现方法 (2)在获取数据类中实现传递接口对象为参数的方法 (3)在主类中调用返回数 ...

  6. java后台两个表关联查询_简单界面+JAVA后台+数据库实现页面对数据库的两张关联表操作...

    前几天写了简单的从页面对数据库进行的操作,下面对该功能进行升级,操作两张相关联的表:上次是对新闻类型的修改,我在这里就不重复了,可以查看我之前的博客, 首先从页面说起: 页面部分: 页面部分我用了10 ...

  7. android 两张电信_两张图片合成一张app-2张图片合成一张app下载1.2.9 官方安卓版-西西软件下载...

    两张图片合成一张app是一款可以把手机上的2张图片合成一张并且看不出是合成的图片的工具,玩转2张图片叠加,可以让你和偶像合照不是梦很逼真的!带你打造与众不同的作品!可以制作双重曝光效果的APP,带你打 ...

  8. Android简单涂鸦以及撤销、重做的实现方法

    前段时间研究了下涂鸦功能的实现,其实单独的涂鸦实现起来还是挺简单的,关键的技术难点是撤销与重做功能的实现.但是这里暂时只说明下涂鸦功能的实现,高手勿喷哈,而且该功能在Android SDK提供的API ...

  9. Android 简单的实现上一张和下一张

    1.准备好两张图片可以多准备几张我只用了两张 2.activity_main.xml布局文件 <?xml version="1.0" encoding="utf-8 ...

最新文章

  1. 关于分布式系统的数据一致性问题(一)
  2. android.mk编译动态库,安卓之Android.mk多文件以及动态库编译
  3. Lvs Tun隧道模式配置
  4. ASP.NET下MVC设计模式的实现
  5. ajax三级联动+全国最新省市县数据
  6. java核心-多线程-Java多线程编程涉及到包、类
  7. element 搜索匹配_分享一个element-ui级联选择器的搜索问题,顺便问下有没有解决方案。...
  8. CSS笔记 - fgm练习 2-10 - 提示框效果 (清除子元素浮动高度塌陷的影响)
  9. 大厂螺丝钉还是开源极客?开源新手该怎么选?
  10. UBNT ER-4 UPnP相关配置
  11. 局域网聊天程序 java MySQL_局域网聊天软件设计与实现(Linux,C++,MySQL)
  12. 卸载ie8的一些方法
  13. FineReport中cjk编码转换
  14. 零代码:如何使用吾来机器人实现表格问答?
  15. 2012年8月26日
  16. 360儿童手表显示服务器错误,360儿童卫士刷机失败怎么办 刷机方法
  17. 支付公司如何赚钱?支付网关如何设计?
  18. 知人者智,自知者明。 胜人者有力,自胜者强。 知足者富。 强行者有志。 不失其所者久。 死而不亡者寿。
  19. 脸书COO桑德伯格:最终塑造我们的,是我们经历的艰难时光
  20. 【Linux】Linux文件锁

热门文章

  1. 一文openpose姿态估计
  2. oracle apex ajax process + dy 校验
  3. [IJCAI2016]Makeup Like a Superstar: Deep Localized Makeup Transfer Network
  4. bat和cmd文件是什么,dos又是什么东西
  5. 万物互联之~网络编程基础篇
  6. Android Studio:使用SVN进行版本控制
  7. Windows下设置桌面壁纸的代码(QT版)
  8. 考研英语 —— 语法和长难句 (2021-07-30)
  9. 程序员副业之如何利用空余时间从博客中赚钱?
  10. Excel-宏、VBA