文章目录

  • 简介
  • 主要功能
  • 如何使用
    • 1. 添加依赖到本地工程
    • 2. 获取权限
    • 3. 初始化UVC业务类,设置UVC摄像头状态回调,设置TextureView或者SurfaceView的Surface监听回调
    • 4. 释放UVC业务类(包含取消UVC摄像头状态回调,停止Camera预览,关闭Camera等操作)
    • 5. 图片抓拍
    • 6. 录制视频
    • 7. 改变摄像机预览参数(包括帧格式、宽度、高度、FPS)
    • 8. 调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数
    • 9.旋转摄像头90度、180度、270度,设置摄像头预览镜像
    • 10.设置多个预览
    • 11.设置多个摄像头(USB2.0受带宽所限,有可能无法同时连接多个摄像头)
  • 其他API
  • 下载演示APK
  • 参考

简介

UVCAndroid是一款用于安卓UVC相机的通用开发库。

GitHub源码地址:https://github.com/shiyinghan/UVCAndroid

主要功能

主要功能包括:
(1) 支持USB Camera设备检测,画面实时预览;
(2) 支持抓拍jpg格式图片,可设置图片压缩质量;
(3) 支持录制mp4格式视频,可屏蔽音频,可设置视频和音频的录制参数;
(4) 支持获取camera支持的分辨率,和分辨率切换;
(5) 支持预览自动识别各种相机的分辨率;
(6) 支持旋转摄像头90度、180度、270度
(7) 支持调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数;
(8) 支持多预览和多摄像头;
(9) 支持Android5.0+;

如何使用

1. 添加依赖到本地工程

第一步 添加mavenCentral仓库到工程gradle文件
Step 1. Add the mavenCentral repository to your build file
Add it in your root build.gradle at the end of repositories:

allprojects {repositories {...mavenCentral()}
}

第二步 添加依赖到app Module的gradle文件

dependencies {implementation 'com.herohan:UVCAndroid:1.0.4'
}

2. 获取权限

Request permissions

     List<String> needPermissions = new ArrayList<>();needPermissions.add(Manifest.permission.CAMERA);needPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);//拍照和录制视频时需要该权限//needPermissions.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE); //Android 11 使用该权限替代 WRITE_EXTERNAL_STORAGEneedPermissions.add(Manifest.permission.RECORD_AUDIO);//录制视频时需要音频时需要该权限//这里使用XXPermissions开源框架获取权限,你也可以使用系统原生的,或者其他开源框架获取权限XXPermissions.with(this).permission(needPermissions).request((permissions, all) -> {if(!all){return;}//摄像头业务操作});
<application...android:requestLegacyExternalStorage="true">

3. 初始化UVC业务类,设置UVC摄像头状态回调,设置TextureView或者SurfaceView的Surface监听回调

Initialize CameraHelper,set UVC Camera state callback

 private ICameraHelper mCameraHelper;private AspectRatioSurfaceView mCameraViewMain;private ICameraHelper.StateCallback mStateListener;//UVC摄像头状态回调mStateListener = new ICameraHelper.StateCallback() {//插入UVC设备@Overridepublic void onAttach(UsbDevice device) {//设置为当前设备(如果没有权限,会显示授权对话框)mCameraHelper.selectDevice(device);}//打开UVC设备成功(也就是已经获取到UVC设备的权限)@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {//打开UVC摄像头mCameraHelper.openCamera();}//打开摄像头成功@Overridepublic void onCameraOpen(UsbDevice device) {//开始预览mCameraHelper.startPreview();//获取预览使用的Size(包括帧格式、宽度、高度、FPS)Size size = mCameraHelper.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//需要自适应摄像头分辨率的话,设置新的宽高比mCameraViewMain.setAspectRatio(width, height);}//添加预览SurfacemCameraHelper.addSurface(mCameraViewMain.getHolder().getSurface(), false);}//关闭摄像头成功@Overridepublic void onCameraClose(UsbDevice device) {if (mCameraHelper != null) {//移除预览SurfacemCameraHelper.removeSurface(mCameraViewMain.getHolder().getSurface());}}//关闭UVC设备成功@Overridepublic void onDeviceClose(UsbDevice device) {}//断开UVC设备@Overridepublic void onDetach(UsbDevice device) {}//用户没有授予访问UVC设备的权限@Overridepublic void onCancel(UsbDevice device) {}};//设置SurfaceView的Surface监听回调mCameraViewMain.getHolder().addCallback(new SurfaceHolder.Callback() {//创建了新的Surface@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelper != null) {//添加预览SurfacemCameraHelper.addSurface(holder.getSurface(), false);}}//Surface发生了改变@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}//销毁了原来的Surface@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelper != null) {//移除预览SurfacemCameraHelper.removeSurface(holder.getSurface());}}});mCameraHelper = new CameraHelper();//设置UVC摄像头状态回调mCameraHelper.setStateCallback(mStateListener);

4. 释放UVC业务类(包含取消UVC摄像头状态回调,停止Camera预览,关闭Camera等操作)

Release CameraHelper(including canceling UVC Camera state callback, stopping Camera preview, etc.)

 mCameraHelper.release();

5. 图片抓拍

Image Capture

 //设置视图片抓拍全局参数(非必须,可以不设置,使用默认值)mCameraHelper.setImageCaptureConfig(mCameraHelper.getImageCaptureConfig().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY));
//        mCameraHelper.setImageCaptureConfig(
//                mCameraHelper.getImageCaptureConfig().setJpegCompressionQuality(90));//设置需要保存图片文件File file = FileUtils.getCaptureFile(this, Environment.DIRECTORY_DCIM, ".jpg");ImageCapture.OutputFileOptions options =new ImageCapture.OutputFileOptions.Builder(file).build();//                ContentValues contentValues = new ContentValues();
//                contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_IMAGE");
//                contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
//
//                ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(
//                        getContentResolver(),
//                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
//                        contentValues).build();//                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//                ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(outputStream).build();//进行图片抓拍mCameraHelper.takePicture(options, new ImageCapture.OnImageCaptureCallback() {//图片抓拍成功@Overridepublic void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {Toast.makeText(TakePictureActivity.this,"save \"" + UriHelper.getPath(TakePictureActivity.this, outputFileResults.getSavedUri()) + "\"",Toast.LENGTH_SHORT).show();}//图片抓拍出现错误@Overridepublic void onError(int imageCaptureError, @NonNull String message, @Nullable Throwable cause) {Toast.makeText(TakePictureActivity.this, message, Toast.LENGTH_SHORT).show();}});

6. 录制视频

Video Capture

 //设置视频录制全局参数(非必须,可以不设置,使用默认值)mCameraHelper.setVideoCaptureConfig(mCameraHelper.getVideoCaptureConfig().setAudioCaptureEnable(true) // true:有音频;false:没有音频(默认为true).setBitRate((int) (1024 * 1024 * 25 * 0.25)).setVideoFrameRate(25).setIFrameInterval(1));//设置需要保存视频文件File file = FileUtils.getCaptureFile(this, Environment.DIRECTORY_MOVIES, ".mp4");VideoCapture.OutputFileOptions options =new VideoCapture.OutputFileOptions.Builder(file).build();//        ContentValues contentValues = new ContentValues();
//        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_VIDEO");
//        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
//
//        VideoCapture.OutputFileOptions options = new VideoCapture.OutputFileOptions.Builder(
//                getContentResolver(),
//                MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
//                contentValues).build();//开始录制mCameraHelper.startRecording(options, new VideoCapture.OnVideoCaptureCallback() {@Overridepublic void onStart() {}//视频录制成功@Overridepublic void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {Toast.makeText(RecordVideoActivity.this,"save \"" + UriHelper.getPath(RecordVideoActivity.this, outputFileResults.getSavedUri()) + "\"",Toast.LENGTH_SHORT).show();}//视频录制出现错误@Overridepublic void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {Toast.makeText(RecordVideoActivity.this, message, Toast.LENGTH_LONG).show();}});

7. 改变摄像机预览参数(包括帧格式、宽度、高度、FPS)

Set camera preview parameters (including frame format, width, height, FPS)

 //停止相机预览mCameraHelper.stopPreview();//设置摄像机预览参数mCameraHelper.setPreviewSize(size);//开始相机预览mCameraHelper.startPreview();//需要自适应摄像头分辨率的话,设置新的宽高比mCameraViewMain.setAspectRatio(mPreviewWidth, mPreviewHeight);

8. 调整对比度、亮度、色调、饱和度、白平衡等等一些相机控制参数

Adjust contrast, brightness, hue, saturation, white balance, and other camera controls

//获取UVCControl对象,通过该对象调整相机控制参数
UVCControl control = mCameraHelper.getUVCControl();//根据监听器设置各种相机控制参数
private void setAllControlChangeListener(UVCControl controls) {// BrightnessisbBrightness.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setBrightness(seekParams.progress));// ContrastisbContrast.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setContrast(seekParams.progress));// Contrast AutocbContrastAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {controls.setContrastAuto(isChecked);});// HueisbHue.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setHue(seekParams.progress));// Hue AutocbHueAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {controls.setHueAuto(isChecked);});// SaturationisbSaturation.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setSaturation(seekParams.progress));// SharpnessisbSharpness.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setSharpness(seekParams.progress));// GammaisbGamma.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setGamma(seekParams.progress));// White BalanceisbWhiteBalance.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setWhiteBalance(seekParams.progress));// White Balance AutocbWhiteBalanceAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {controls.setWhiteBalanceAuto(isChecked);});// Backlight CompensationisbBacklightComp.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setBacklightComp(seekParams.progress));// GainisbGain.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setGain(seekParams.progress));// Exposure TimeisbExposureTime.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setExposureTimeAbsolute(seekParams.progress));// Exposure Time AutocbExposureTimeAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {controls.setExposureTimeAuto(isChecked);});// IrisisbIris.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setIrisAbsolute(seekParams.progress));// FocusisbFocus.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setFocusAbsolute(seekParams.progress));// Focus AutocbFocusAuto.setOnCheckedChangeListener((buttonView, isChecked) -> {controls.setFocusAuto(isChecked);});// ZoomisbZoom.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setZoomAbsolute(seekParams.progress));// PanisbPan.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setPanAbsolute(seekParams.progress));// TiltisbTilt.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setTiltAbsolute(seekParams.progress));// RollisbRoll.setOnSeekChangeListener((MyOnSeekChangeListener) seekParams -> controls.setRollAbsolute(seekParams.progress));// Power Line FrequencyrgPowerLineFrequency.setOnCheckedChangeListener((group, checkedId) -> {int value = 0;if (checkedId == R.id.rbPowerLineFrequencyDisable) {value = 0;} else if (checkedId == R.id.rbPowerLineFrequency50Hz) {value = 1;} else if (checkedId == R.id.rbPowerLineFrequency60Hz) {value = 2;} else if (checkedId == R.id.rbPowerLineFrequencyAuto) {value = 3;}controls.setPowerlineFrequency(value);});}// 重置所有相机控制参数为初试值
private void resetAllControlParams(UVCControl control) {// Brightnesscontrol.resetBrightness();// Contrastcontrol.resetContrast();// Contrast Autocontrol.resetContrastAuto();// Huecontrol.resetHue();// Hue Autocontrol.resetHueAuto();// Saturationcontrol.resetSaturation();// Sharpnesscontrol.resetSharpness();// Gammacontrol.resetGamma();// White Balancecontrol.resetWhiteBalance();// White Balance Autocontrol.resetWhiteBalanceAuto();// Backlight Compensationcontrol.resetBacklightComp();// Gaincontrol.resetGain();// Exposure Timecontrol.resetExposureTimeAbsolute();// Auto-Exposure Modecontrol.resetAutoExposureMode();// Iriscontrol.resetIrisAbsolute();// Focuscontrol.resetFocusAbsolute();// Focus Autocontrol.resetFocusAuto();// Zoomcontrol.resetZoomAbsolute();// Pancontrol.resetPanAbsolute();// Tiltcontrol.resetTiltAbsolute();// Rollcontrol.resetRollAbsolute();// Power Line Frequencycontrol.resetPowerlineFrequency();}

9.旋转摄像头90度、180度、270度,设置摄像头预览镜像

Rotate the camera 90 degrees, 180 degrees, and 270 degrees , set the camera preview mirror

 //旋转摄像头private void rotateBy(int angle) {mPreviewRotation += angle;mPreviewRotation %= 360;if (mPreviewRotation < 0) {mPreviewRotation += 360;}if (mCameraHelper != null) {mCameraHelper.setPreviewConfig(mCameraHelper.getPreviewConfig().setRotation(mPreviewRotation));}}//设置水平镜像显示private void flipHorizontally() {if (mCameraHelper != null) {mCameraHelper.setPreviewConfig(mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_HORIZONTAL));}}//设置垂直镜像显示private void flipVertically() {if (mCameraHelper != null) {mCameraHelper.setPreviewConfig(mCameraHelper.getPreviewConfig().setMirror(MirrorMode.MIRROR_VERTICAL));}}

10.设置多个预览

Set multiple previews

mCameraHelper.addSurface(svCameraViewMain.getHolder().getSurface(), false);
mCameraHelper.addSurface(svCameraViewSecond.getHolder().getSurface(), false);
mCameraHelper.addSurface(svCameraViewThird.getHolder().getSurface(), false);
mCameraHelper.addSurface(svCameraViewFourth.getHolder().getSurface(), false);

11.设置多个摄像头(USB2.0受带宽所限,有可能无法同时连接多个摄像头)

Setting multiple Cameras

 private ICameraHelper mCameraHelperLeft;private ICameraHelper mCameraHelperRight;private AspectRatioSurfaceView svCameraViewLeft;private AspectRatioSurfaceView svCameraViewRight;private UsbDevice mUsbDeviceLeft;private UsbDevice mUsbDeviceRight;private final ICameraHelper.StateCallback mStateListenerLeft = new ICameraHelper.StateCallback() {@Overridepublic void onAttach(UsbDevice device) {synchronized (mSync) {if (mUsbDeviceLeft == null && !device.equals(mUsbDeviceRight)) {selectDeviceLeft(device);}}}@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {if (device.equals(mUsbDeviceLeft)) {UVCParam param = new UVCParam();param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH);mCameraHelperLeft.openCamera(param);}}@Overridepublic void onCameraOpen(UsbDevice device) {if (device.equals(mUsbDeviceLeft)) {mCameraHelperLeft.startPreview();Size size = mCameraHelperLeft.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//auto aspect ratiosvCameraViewLeft.setAspectRatio(width, height);}mCameraHelperLeft.addSurface(svCameraViewLeft.getHolder().getSurface(), false);mIsCameraLeftConnected = true;}}@Overridepublic void onCameraClose(UsbDevice device) {if (device.equals(mUsbDeviceLeft)) {if (mCameraHelperLeft != null) {mCameraHelperLeft.removeSurface(svCameraViewLeft.getHolder().getSurface());}mIsCameraLeftConnected = false;}}@Overridepublic void onDeviceClose(UsbDevice device) {}@Overridepublic void onDetach(UsbDevice device) {if (device.equals(mUsbDeviceLeft)) {mUsbDeviceLeft = null;}}@Overridepublic void onCancel(UsbDevice device) {if (device.equals(mUsbDeviceLeft)) {mUsbDeviceLeft = null;}}};private final ICameraHelper.StateCallback mStateListenerRight = new ICameraHelper.StateCallback() {@Overridepublic void onAttach(UsbDevice device) {synchronized (mSync) {if (mUsbDeviceRight == null && !device.equals(mUsbDeviceLeft)) {selectDeviceRight(device);}}}@Overridepublic void onDeviceOpen(UsbDevice device, boolean isFirstOpen) {if (device.equals(mUsbDeviceRight)) {UVCParam param = new UVCParam();param.setQuirks(UVCCamera.UVC_QUIRK_FIX_BANDWIDTH);mCameraHelperRight.openCamera(param);}}@Overridepublic void onCameraOpen(UsbDevice device) {if (device.equals(mUsbDeviceRight)) {mCameraHelperRight.startPreview();Size size = mCameraHelperRight.getPreviewSize();if (size != null) {int width = size.width;int height = size.height;//auto aspect ratiosvCameraViewRight.setAspectRatio(width, height);}mCameraHelperRight.addSurface(svCameraViewRight.getHolder().getSurface(), false);mIsCameraRightConnected = true;}}@Overridepublic void onCameraClose(UsbDevice device) {if (device.equals(mUsbDeviceRight)) {if (mCameraHelperRight != null) {mCameraHelperRight.removeSurface(svCameraViewRight.getHolder().getSurface());}mIsCameraRightConnected = false;}}@Overridepublic void onDeviceClose(UsbDevice device) {}@Overridepublic void onDetach(UsbDevice device) {if (device.equals(mUsbDeviceRight)) {mUsbDeviceRight = null;}}@Overridepublic void onCancel(UsbDevice device) {if (device.equals(mUsbDeviceRight)) {mUsbDeviceRight = null;}}};svCameraViewLeft.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelperLeft != null) {mCameraHelperLeft.addSurface(holder.getSurface(), false);}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelperLeft != null) {mCameraHelperLeft.removeSurface(holder.getSurface());}}});svCameraViewRight.setAspectRatio(DEFAULT_WIDTH, DEFAULT_HEIGHT);svCameraViewRight.getHolder().addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {if (mCameraHelperRight != null) {mCameraHelperRight.addSurface(holder.getSurface(), false);}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {if (mCameraHelperRight != null) {mCameraHelperRight.removeSurface(holder.getSurface());}}});mCameraHelperLeft = new CameraHelper();mCameraHelperLeft.setStateCallback(mStateListenerLeft);mCameraHelperRight = new CameraHelper();mCameraHelperRight.setStateCallback(mStateListenerRight);

其他API

方法 说明
getDeviceList() 获取当前检测到的所有UVC设备
getSupportedFormatList() 获取当前摄像头支持的Format列表
getSupportedSizeList() 获取当前摄像头支持的Size列表
getPreviewSize() 获取当前摄像头正在使用的预览Size
setButtonCallback() 设置按钮事件回调
setFrameCallback() 设置实时预览图像数据回调(请在StateCallback的onDeviceOpen或者onCameraOpen回调函数里面调用,使用方法可以参考demo里面的SetFrameCallbackActivity),支持格式 UVCCamera.PIXEL_FORMAT_YUV;PIXEL_FORMAT_NV12;PIXEL_FORMAT_NV21;PIXEL_FORMAT_RGB565;PIXEL_FORMAT_RGBX等格式
openCamera(Size size) 用指定格式打开当前摄像头
closeCamera() 关闭当前摄像头
isRecording() 是否正在录像
isCameraOpened() 是否已经打开当前摄像头

下载演示APK

Download demo APK

app-release.apk


demo-release.apk

参考

saki4510t/UVCCamera

UVCAndroid,安卓UVC相机通用开发库(支持多预览和多摄像头)相关推荐

  1. 从零开始安卓端相机功能开发(一)了解用什么去开发以及流程

    目前已有章节大家可以去学习也可以参考一下 1.从零开始安卓端相机功能开发(一)了解用什么去开发以及流程 2.从零开始安卓端相机功能开发(二)让我们来开发一个相机 3.从零开始开发Android相机ap ...

  2. 从零开始安卓端相机功能开发(二)让我们来开发一个相机

    目前已有章节大家可以去学习也可以参考一下 1.从零开始安卓端相机功能开发(一)了解用什么去开发以及流程 2.从零开始安卓端相机功能开发(二)让我们来开发一个相机 3.从零开始开发Android相机ap ...

  3. Android相机支持的预览格式详解

    当我们想从相机的预览中拿到原始的图像用于处理,我们就要先设置相机的参数,让它输出我们期望的格式.本篇文档旨在解释清楚相机支持的预览格式,相机默认预览格式. 随便找了一台Android手机,通过log输 ...

  4. Android 音视频开发(三) -- Camera2 实现预览、拍照功能

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

  5. Android 音视频开发(二) -- Camera1 实现预览、拍照功能

    音视频 系列文章 Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍 ...

  6. html5 图片上传,支持图片预览、压缩、及进度显示,兼容IE6+及标准浏览器

    原文:html5 图片上传,支持图片预览.压缩.及进度显示,兼容IE6+及标准浏览器 以前写过上传组件,见 打造 html5 文件上传组件,实现进度显示及拖拽上传,兼容IE6+及其它标准浏览器,对付一 ...

  7. (0094)iOS开发之本地文件预览的三种方法(2)

    (0090)iOS开发之本地文件预览的三种方法(1) (0095)iOS开发之本地文件预览的三种方法(3) QuickLook预览文件 quickLook预览文件也是系统提供的预览方法,具体使用如下 ...

  8. (0095)iOS开发之本地文件预览的三种方法(3)

    (0090)iOS开发之本地文件预览的三种方法(1) (0094)iOS开发之本地文件预览的三种方法(2) 用功能强大的Webview来实现文件预览功能 我导入的 ios.pdf 前两种可以打开,但是 ...

  9. 模仿微信朋友圈 图片浏览 js javascript 支持图片预览,滑动切换,双指缩放,图片缓存

    模仿微信朋友圈 图片浏览 js javascript 支持图片预览,滑动切换,双指缩放,图片缓存 2017年08月10日 12:11:38 阅读数:2311 previewImage-mobile 仿 ...

最新文章

  1. 线程的挂起是错误的概念实际是线程的阻塞,挂起只针对进程,将进程挂起会将进程从内存空间交换到磁盘空间的过程
  2. 第4章javascript变量、作用域和内存回收
  3. 最强代码生成器平台,杀疯了!
  4. 2017.10.8 软件工程----总体设计
  5. 64位LINUX下hadoop2.2.0重新编译及安装步骤
  6. linux qos 软件,linux下QOS:应用篇 - 博客 - 伯乐在线
  7. 为防盗装自动门 不想也会影响生活
  8. x=min(x, y)
  9. [saiku] JCR在saiku中的运用原理
  10. 声音模仿_澳洲这种鸟堪称“超级声音模仿秀”,比八哥还牛,却正遭山火毁灭...
  11. 查找功能_苹果查找功能怎么查找另一台设备?很简单,只需这样操作
  12. pycharm ubuntu 安装_pycharm使用远程python解释器
  13. python调用按键精灵插件_[良心教程]分享最新最实用的按键精灵封装函数
  14. blazeds_Spring BlazeDS集成:它是什么,它会发生什么变化?
  15. iPad入手必备手册——iPad Mini 使用手册(适用于iOS 6软件 )
  16. 记录一个小程序 input输入框格式手机号方法
  17. 使用NOKIA MMS LIBRARY发送中国移动彩信
  18. MFC之学习扇形绘制与绘制阴阳鱼图
  19. 并发控制五(封锁的粒度)
  20. 计算机按键 shift的作用,电脑shift键的十一个妙用

热门文章

  1. Java查找字符串最后一次出现的位置
  2. 色彩运用:10个最新的黑色风格网站作品
  3. 再搞CRUD,就真的变成废物獠!
  4. lodash中get方法
  5. 目标检测实战必会!4种基于YOLO目标检测(Python和C++两种版本实现)
  6. Grasp Pose Detection with Affordance-based Task Constraint Learning in Single-view Point Clouds
  7. struts开发包下载地址
  8. 【CSDN】每日一练。
  9. 【Python Onramp】 0. 卷首语:项目导向,或Learn by doing
  10. 不需权限获得Android设备唯一标识序列号