Android素描算法及实现手指在图片上左右滑动调节图片透明度,最终实现类似调节素描浓度的效果

一、前期基础知识储备

UI掌握PS这一逆天的软件,可以实现将图片转化为素描或者水彩的效果,以素描为例:

  1. 在Photoshop中打开一张人物照片,按下快捷键“Ctrl+Shift+U”,把它转换成黑白颜色;
  2. 复制图层,得到一个副本图层。按下快捷键“Ctrl+I”,将副本图层转换成负片效果;
  3. 将副本图层下拉菜单选为“颜色减淡”,这时图片会亮得几乎什么也看不见,不要急,慢慢来;
  4. 在“滤镜”菜单下选择“模糊→高斯模糊”,模糊半径值可根据你需要的素描线条粗细深浅来设置。到此素描画像工作就完成了。

我们将以上四步进行抽象,得到将图片转为素描效果的步骤即为:

  1. 去色,将图片变为灰度图,即黑白图;
  2. 反相,得到每个像素的补色,具体效果就像照片的底片;
  3. 高斯模糊,把反相后的像素值平均一下;
  4. 颜色减淡,将第1步中的像素和第3步得到的像素值进行计算。

在Android图像处理领域,我们可以使用像素点分析的方法实现上述的效果组合。下面,用代码实现上述过程

二、上代码,具体实现素描算法

1)去色,获取黑白图;

	public static int[] discolor(Bitmap bitmap) {
 
		int picHeight = bitmap.getHeight();
		int picWidth = bitmap.getWidth();
 
		int[] pixels = new int[picWidth * picHeight];
		bitmap.getPixels(pixels, 0, picWidth, 0, 0, picWidth, picHeight);
 
		for (int i = 0; i < picHeight; ++i) {
			for (int j = 0; j < picWidth; ++j) {
				int index = i * picWidth + j;
				int color = pixels[index];
				int r = (color & 0x00ff0000) >> 16;
				int g = (color & 0x0000ff00) >> 8;
				int b = (color & 0x000000ff);
				int grey = (int) (r * KR + g * KG + b * KB);
				pixels[index] = grey << 16 | grey << 8 | grey | 0xff000000;
			}
		}
		
		return pixels;
 
	}

2)反相,得到图片的底图;

public static int[] reverseColor(int[] pixels) {
		
		int length = pixels.length;
		int[] result = new int[length];
		for (int i = 0; i < length; ++i) {
			int color = pixels[i];
			
			int r = 255 - (color & 0x00ff0000) >> 16;
			int g = 255 - (color & 0x0000ff00) >> 8;
			int b = 255 - (color & 0x000000ff);
			result[i] = r << 16 | g << 8 | b | 0xff000000;
		}
		return result;
		
	}

3)高斯模糊,得到反高斯图像;

	public static void gaussBlur(int[] data, int width, int height, int radius,
			float sigma) {
 
		float pa = (float) (1 / (Math.sqrt(2 * Math.PI) * sigma));
		float pb = -1.0f / (2 * sigma * sigma);
 
		// generate the Gauss Matrix
		float[] gaussMatrix = new float[radius * 2 + 1];
		float gaussSum = 0f;
		for (int i = 0, x = -radius; x <= radius; ++x, ++i) {
			float g = (float) (pa * Math.exp(pb * x * x));
			gaussMatrix[i] = g;
			gaussSum += g;
		}
 
		for (int i = 0, length = gaussMatrix.length; i < length; ++i) {
			gaussMatrix[i] /= gaussSum;
		}
 
		// x direction
		for (int y = 0; y < height; ++y) {
			for (int x = 0; x < width; ++x) {
				float r = 0, g = 0, b = 0;
				gaussSum = 0;
				for (int j = -radius; j <= radius; ++j) {
					int k = x + j;
					if (k >= 0 && k < width) {
						int index = y * width + k;
						int color = data[index];
						int cr = (color & 0x00ff0000) >> 16;
						int cg = (color & 0x0000ff00) >> 8;
						int cb = (color & 0x000000ff);
 
						r += cr * gaussMatrix[j + radius];
						g += cg * gaussMatrix[j + radius];
						b += cb * gaussMatrix[j + radius];
 
						gaussSum += gaussMatrix[j + radius];
					}
				}
 
				int index = y * width + x;
				int cr = (int) (r / gaussSum);
				int cg = (int) (g / gaussSum);
				int cb = (int) (b / gaussSum);
				
				data[index] = cr << 16 | cg << 8 | cb | 0xff000000;
			}
		}
 
		// y direction
		for (int x = 0; x < width; ++x) {
			for (int y = 0; y < height; ++y) {
				float r = 0, g = 0, b = 0;
				gaussSum = 0;
				for (int j = -radius; j <= radius; ++j) {
					int k = y + j;
					if (k >= 0 && k < height) {
						int index = k * width + x;
						int color = data[index];
						int cr = (color & 0x00ff0000) >> 16;
						int cg = (color & 0x0000ff00) >> 8;
						int cb = (color & 0x000000ff);
 
						r += cr * gaussMatrix[j + radius];
						g += cg * gaussMatrix[j + radius];
						b += cb * gaussMatrix[j + radius];
 
						gaussSum += gaussMatrix[j + radius];
					}
				}
 
				int index = y * width + x;
				int cr = (int) (r / gaussSum);
				int cg = (int) (g / gaussSum);
				int cb = (int) (b / gaussSum);
				data[index] = cr << 16 | cg << 8 | cb | 0xff000000;
			}
		}
	}

4)淡化颜色,生成Sketch图

	public static void colorDodge(int[] baseColor, int[] mixColor) {
 
		for (int i = 0, length = baseColor.length; i < length; ++i) {
			int bColor = baseColor[i];
			int br = (bColor & 0x00ff0000) >> 16;
			int bg = (bColor & 0x0000ff00) >> 8;
			int bb = (bColor & 0x000000ff);
 
			int mColor = mixColor[i];
			int mr = (mColor & 0x00ff0000) >> 16;
			int mg = (mColor & 0x0000ff00) >> 8;
			int mb = (mColor & 0x000000ff);
 
			int nr = colorDodgeFormular(br, mr);
			int ng = colorDodgeFormular(bg, mg);
			int nb = colorDodgeFormular(bb, mb);
 
			baseColor[i] = nr << 16 | ng << 8 | nb | 0xff000000;
		}
 
	}
 
	private static int colorDodgeFormular(int base, int mix) {
		
		int result = base + (base * mix) / (255 - mix);
		result = result > 255 ? 255 : result;
		return result;
		
	}

5)将上述代码封如工具类中,最后定义一个获取素描图的方法

	public static Bitmap testGaussBlur(Bitmap src, int r, int fai) {
 
		int width = src.getWidth();
		int height = src.getHeight();
 
		int[] pixels = Sketch.discolor(src);
		int[] copixels = Sketch.simpleReverseColor(pixels);
		Sketch.simpleGaussBlur(copixels, width, height, r, fai);
		Sketch.simpleColorDodge(pixels, copixels);
 
		Bitmap bitmap = Bitmap.createBitmap(pixels, width, height,
				Config.RGB_565);
		return bitmap;
 
	}

外界,调用时,传入两个int类型的参数即可,表示高斯模糊的程度和半径。

public class SketchActivity extends AppCompatActivity {
    private static final String TAG = "SketchActivity";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sketch);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;

        ImageView imageView = findViewById(R.id.test_img);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.timg);
        // 对大图进行压缩 传入的参数为手机屏幕的尺寸 工具类在文末给出
        bitmap = Utils.compressBySampleSize(bitmap, width, height, false);

        Bitmap bitmap = SketchUtil.testGaussBlur(bitmap,10,10);
        imageView.setImageBitmap(bitmap); // 素描图
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

效果如下:

三、上代码,具体实现手指在图片上左右滑动调节图片透明度,实现类似调节素描浓度的效果

在上面的算法中,我们为testGaussBlur()传入两个参数,即可实现调节素描的效果。现在提出另一个思路,就是在不改变已传入参数的前提下,调节素描的浓度:

要求:手指左滑浓度变淡,即素描图变透明;手指右滑变浓,即凸显素描图

① 我们在原有ImageView的位置放入一个FrameLayout,然后在这个FrameLayout中放入两个ImageView,一个用于放置原图,在底层;一个用于放置素描效果图,在上层。两个ImageView的大小设为一样大,由于FrameLayout的特性,我们只能看见效果图。

② 然后我们为上层放置效果图的ImageView写入手势控制事件,当用户在图片上左右移动手指的时候,就调节上层图片的透明度,这样就达到了类似调节素描浓度的效果。

实现如下:

/**
 * 作者    cpf
 * 时间    2019/4/16
 * 文件    TestApplication
 * 描述    手指在图片上滑动 可调节图片的透明度
 * ①使用Seekbar是否可以做到? 没有办法区分左右滑动 记录上次滑动结果可以做的到 在onStopTrackingTouch方法中记录
 * ②自定义View + 手势移动 onFling没法表示中间的过程 只有起始和结束时的状态 ,需要一个渐变的距离 onScroll
 */
public class SeekbarActivity extends AppCompatActivity implements View.OnTouchListener {
    private static final String TAG = "SeekbarActivity";

    private ImageView mImageView, mGestureImageView, originImageView;
    private SeekBar mSeekBar;
    private TextView mTxt, mGesTxt;
    private int mProgress = 100;
    private float mDistance = 1, maxDistance = 100; // 控制progress参数在0-100,0为完全透明 100为起始值可见
    private GestureDetector mGestureDetector;
    private Bitmap finalBitmap,bitmap;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_seekbar);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;

        mImageView = findViewById(R.id.alpha_img);
        mGestureImageView = findViewById(R.id.ges_alpha_img);
        originImageView = findViewById(R.id.origin_img);
        mSeekBar = findViewById(R.id.alpha_seek);
        mTxt = findViewById(R.id.alpha_txt);
        mGesTxt = findViewById(R.id.ges_alpha_txt);
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);
        bitmap = Utils.compressBySampleSize(bitmap, width, height, false);

        finalBitmap = SketchUtil.testGaussBlur(bitmap,10,10);
        mImageView.setImageBitmap(bitmap);
        mGestureImageView.setImageBitmap(finalBitmap);
        originImageView.setImageBitmap(bitmap);
        mSeekBar.setMax(100); // 100 代表完不全透明 0 代表完全透明
        mSeekBar.setProgress(100);
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                Log.d(TAG, "onProgressChanged: direction..." + direction);
                if (direction == 0) {
                    // 左滑 变透明
                    if ((mProgress - progress) > 0 && (mProgress - progress) < 100) {
                        mImageView.setImageBitmap(SketchUtil.testGaussBlur(Utils.setAlpha(finalBitmap, (mProgress - progress)),10,10));
                        mTxt.setText(String.valueOf((mProgress - progress)) + "%");
                        Log.d(TAG, "onProgressChanged: 左滑," + (mProgress - progress));
                    } else if ((mProgress - progress) < 0) {
                        mImageView.setImageBitmap(Utils.setAlpha(SketchUtil.testGaussBlur(finalBitmap,10,10), 1));
                        mTxt.setText(String.valueOf(1) + "%");
                        Log.d(TAG, "onProgressChanged: 左滑过界," + 1);
                    }
                } else {
                    // 右滑 变清晰
                    if ((mProgress + progress) < 100 && (mProgress + progress) > 0) {
                        mImageView.setImageBitmap(SketchUtil.testGaussBlur(Utils.setAlpha(finalBitmap, (mProgress + progress)),10,10));
                        mTxt.setText(String.valueOf((mProgress + progress)) + "%");
                        Log.d(TAG, "onProgressChanged: 右滑," + (mProgress + progress));
                    } else if ((mProgress + progress) > 100) {
                        mImageView.setImageBitmap(Utils.setAlpha(SketchUtil.testGaussBlur(finalBitmap,10,10), 100));
                        mTxt.setText(String.valueOf(100) + "%");
                        Log.d(TAG, "onProgressChanged: 右滑过界," + 100);
                    }
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                mTxt.setVisibility(View.GONE);
                mProgress = seekBar.getProgress();
                Log.d(TAG, "onProgressChanged, onStopTrackingTouch:::" + mProgress);
            }
        });
        //初始化图片
        mImageView.setImageBitmap(Utils.setAlpha(finalBitmap, 100)); // 0 代表完全透明

        initGesture();
    }

    private void initGesture() {
        mGestureDetector = new GestureDetector(new simpleGestureListener());
        // 注意以下四个声明不可缺少
        mGestureImageView.setOnTouchListener(this);
        mGestureImageView.setFocusable(true);
        mGestureImageView.setClickable(true);
        mGestureImageView.setLongClickable(true);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        return mGestureDetector.onTouchEvent(event);
    }

    private int direction = 0;

    private class simpleGestureListener extends
            GestureDetector.SimpleOnGestureListener {

        /*****OnGestureListener的函数*****/
        final int FLING_MIN_DISTANCE = 10;
        final float MIN_DISTANCE = 0, MAX_DISTANCE = 100;

        public boolean onDown(MotionEvent e) {
            // do nothing
            return false;
        }

        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                                float distanceX, float distanceY) {
            if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE) {
                // Fling left 左滑变透明 distanceX为正值
                int progress = (int) (maxDistance - distanceX / 15);
                if (progress >= MIN_DISTANCE) {
                    mGestureImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));
                    maxDistance = progress;
                    mProgress = progress;
                    mGesTxt.setVisibility(View.VISIBLE);
                    mGesTxt.setText(String.valueOf(progress) + "%");
                    Log.d("MyGesture22", "onScroll:" + progress + ", 左滑 ," + distanceX);
                }

            } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE) {
                // Fling right 右滑显示 distanceX为负值
                int progress = (int) (maxDistance - distanceX / 5);
                if (progress <= MAX_DISTANCE) {
                    mGestureImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));
                    maxDistance = progress;
                    mProgress = progress;
                    mGesTxt.setVisibility(View.VISIBLE);
                    mGesTxt.setText(String.valueOf(progress) + "%");
                    Log.d("MyGesture22", "onScroll:" + progress + ", 右滑 ," + distanceX);
                }
            }
            return true;
        }

        // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                               float velocityY) {
            mGesTxt.setVisibility(View.GONE);
            mImageView.setImageBitmap(Utils.xFerMode(bitmap,Utils.setAlpha(finalBitmap, mProgress)));
            Log.d(TAG, "onFling: " + mProgress);
            return true;
        }
    }
}

可以看到,笔者先是尝试使用SeekBar的方式来实现类似的效果,即将该SeekBar置于图片的上层,然后重写其两个属性,将其设为完全透明,即对于用户不可见,同时将SeekBar设为大小同ImageView一样大,也就实现了对于图片的覆盖,然后在对SeekBar的滑动监听事件onProgressChanged()中调节效果图的透明度,同时在另一监听方法onStopTrackingTouch()记录手指上次离开屏幕时的数值,这样在下次手指down下的时候,传入该值即可。用SeekBar实现这一需求只能做到一半,如上面代码中的注视一般,可以做到记录上次滑动的数值,但是没有办法区分开手指滑动的方向,即无法验证手指左滑还是右滑。

而后,笔者采用GestureDetector的方式实现,拦截下屏幕上的触摸事件,并把事件设置给展示效果图的ImageView。这样手指滑动的事件就传递给了ImageView。然后在重写onScroll()方法,区分开手指左右滑动,并在对应的实现中对图片透明度做不同的调整;最后重写onFling()方法,识别手指抬起,将展示图片透明度数值的TextView设为不可见。

效果如下:

以下是文中使用到的工具类:用以加载大图、调节图片透明度。还有其他一些关于Bitmap的处理方法。

public class Utils {
    public static final String TAG = "Utils:";

    private static final String SAVE_FOLDER = "PencilCamera";
    private static final String SAVE_FILENAME_PREFIX = "IMG";

    private static int lastSaveFileIndex = 0;

    /**
     * 图片透明度处理
     *
     * @param sourceImg 原始图片
     * @param number    透明度
     * @return
     */
    public static Bitmap setAlpha(Bitmap sourceImg, int number) {
        try {
            int[] argb = new int[sourceImg.getWidth() * sourceImg.getHeight()];
            sourceImg.getPixels(argb, 0, sourceImg.getWidth(), 0, 0,
                    sourceImg.getWidth(), sourceImg.getHeight());// 获得图片的ARGB值
            number = number * 255 / 100;
            for (int i = 0; i < argb.length; i++) {
                if ((argb[i] & 0xff000000) != 0x00000000) {// 透明色不做处理
                    argb[i] = (number << 24) | (argb[i] & 0xFFFFFF);// 修改最高2位的值
                }
            }
            sourceImg = Bitmap.createBitmap(argb, sourceImg.getWidth(),
                    sourceImg.getHeight(), Bitmap.Config.ARGB_8888);
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            System.gc();
        }
        return sourceImg;
    }

    /**
     * 混合两张Bitmap 返回融合后的Bitmap
     *
     * @param src 主图
     * @param dst 修饰图
     */
    public static Bitmap xFerMode(Bitmap src, Bitmap dst) {
        Bitmap lightenModeBitmap = Bitmap.createBitmap(dst.getWidth(), dst.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(lightenModeBitmap);
        Paint paint1 = new Paint();
        paint1.setAntiAlias(true);
        Rect srcRect = new Rect(0, 0, src.getWidth(), src.getHeight());
        canvas.drawARGB(0, 0, 0, 0);
        canvas.drawBitmap(src, srcRect, srcRect, paint1); //画人物 src
        paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
        // 再把原来的bitmap画到现在的bitmap
        canvas.drawBitmap(dst, srcRect, srcRect, paint1); //画特效 dst
        return lightenModeBitmap;
    }

    /**
     * Return the compressed bitmap using sample size.
     *
     * @param src       The source of bitmap.
     * @param maxWidth  The maximum width.
     * @param maxHeight The maximum height.
     * @param recycle   True to recycle the source of bitmap, false otherwise.
     * @return the compressed bitmap
     */
    public static Bitmap compressBySampleSize(final Bitmap src,
                                              final int maxWidth,
                                              final int maxHeight,
                                              final boolean recycle) {
        if (isEmptyBitmap(src)) return null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = baos.toByteArray();
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
        options.inJustDecodeBounds = false;
        if (recycle && !src.isRecycled()) src.recycle();
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    }

    /**
     * Return the sample size.
     * 1500*1500的大图采样率为8 尺寸为562*562
     * 300*400的采样率为2
     * @param options   The options.
     * @param maxWidth  The maximum width.
     * @param maxHeight The maximum height.
     * @return the sample size
     */
    private static int calculateInSampleSize(final BitmapFactory.Options options,
                                             final int maxWidth,
                                             final int maxHeight) {
        int height = options.outHeight;
        int width = options.outWidth;
        int inSampleSize = 1;
        while (height > maxHeight || width > maxWidth) {
            height >>= 1;
            width >>= 1;
            inSampleSize <<= 1;
        }
        return inSampleSize;
    }

    private static boolean isEmptyBitmap(final Bitmap src) {
        return src == null || src.getWidth() == 0 || src.getHeight() == 0;
    }

    /**
     * Reize bitmap with dimensions equal to or less than given params, without changing the aspect ratio
     * @param bitmap    Input bitmap
     * @param maxWidth  Max allowed width of resized Bitmap
     * @param maxHeight Max allowed height of resized Bitmap
     * @return Resized bitmap
     */
    public static Bitmap resizeBitmap(Bitmap bitmap, int maxWidth, int maxHeight) {
        if (maxHeight > 0 && maxWidth > 0) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            float ratioBitmap = (float) width / (float) height;
            float ratioMax = (float) maxWidth / (float) maxHeight;

            int finalWidth = maxWidth;
            int finalHeight = maxHeight;
            if (ratioMax > ratioBitmap) {
                finalWidth = (int) ((float)maxHeight * ratioBitmap);
            } else {
                finalHeight = (int) ((float)maxWidth / ratioBitmap);
            }
            Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, finalWidth, finalHeight, true);
            return scaledBitmap;
        } else {
            return bitmap;
        }
    }

    /**
     * Save bitmap as JPEG with a incremental filename, in the SAVE_FOLDER directory
     * @param activity
     * @param bitmap
     * @return
     * @throws IOException
     */
    public static String saveBitmap(Activity activity, Bitmap bitmap) throws IOException {
        File file;
        try {
            // Create a new save file
            file = createSaveFile();
            OutputStream fOut = new FileOutputStream(file);
            // saving the Bitmap to a file compressed as a JPEG with 85% compression rate
            bitmap.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
            fOut.close();

            // Add saved file to gallery
            Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri contentUri = Uri.fromFile(file);
            mediaScanIntent.setData(contentUri);
            activity.sendBroadcast(mediaScanIntent);

        } catch(IOException e) {
            String errorMsg = "Unable to save image";
            Log.e(TAG, errorMsg + ":" + e.getMessage());
            throw new IOException(errorMsg);
        }
        return file.getPath();
    }

    /**
     * Rotate bitmap by given angle in degrees
     * @param bitmap
     * @param angle
     * @return
     */
    public static Bitmap rotateBitmap(Bitmap bitmap, int angle) {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    /**
     * Create a new save file in the SAVE_FOLDER directory with name as '<SAVE_FILENAME_PREFIX>_<3 digit serial number>'
     * ( example: IMG_001)
     * @return
     * @throws IOException
     */
    private static File createSaveFile() throws IOException {
        File file;
        String path = Environment.getExternalStorageDirectory().toString();
        OutputStream fOut = null;
        String saveFolderPath = path + '/' + SAVE_FOLDER;
        int saveFileIndex = lastSaveFileIndex;
        do {
            String saveFileName = SAVE_FILENAME_PREFIX + String.format("%03d", ++saveFileIndex) + ".jpg";
            String saveFilePath = saveFolderPath + '/' + saveFileName;
            Log.d(TAG,"Saving image to path - "+saveFilePath);
            file = new File(saveFilePath); // the File to save to
            file.getParentFile().mkdirs();
        } while(file.exists());
        file.createNewFile();
        return file;
    }
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值