网站备案跟域名备案政协网站法治建设
2026/4/8 22:15:31 网站建设 项目流程
网站备案跟域名备案,政协网站法治建设,网站建设 h5 小程序,网站建设服务哪家好 价格多少钱1. Android相机与相册开发基础 在移动应用开发中#xff0c;相机和相册功能是最常用的基础能力之一。无论是社交应用的头像上传#xff0c;还是电商平台的商品评价#xff0c;都离不开图片的拍摄和选择。作为Android开发者#xff0c;掌握这两个功能的实现原理和技巧至关重…1. Android相机与相册开发基础在移动应用开发中相机和相册功能是最常用的基础能力之一。无论是社交应用的头像上传还是电商平台的商品评价都离不开图片的拍摄和选择。作为Android开发者掌握这两个功能的实现原理和技巧至关重要。记得我第一次实现相机功能时遇到了一个典型问题拍完照片后无法在相册中显示。后来发现是因为没有发送系统广播通知媒体库更新。这种看似简单的功能点往往藏着不少技术细节。1.1 核心组件与权限实现相机和相册功能主要涉及以下几个关键组件Camera APIAndroid系统提供的相机服务接口MediaStore管理系统媒体文件的ContentProviderFileProvider安全共享应用私有文件的特殊ContentProviderIntent用于启动系统相机和相册界面在AndroidManifest.xml中需要声明以下权限uses-permission android:nameandroid.permission.CAMERA/ uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE/ uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE/从Android 6.0开始这些危险权限需要在运行时动态申请。我建议使用Google提供的ActivityResult API来处理权限请求它比传统的onRequestPermissionsResult更简洁// 相机权限请求 ActivityResultLauncherString cameraPermission registerForActivityResult( new ActivityResultContracts.RequestPermission(), granted - { if (granted) { startCamera(); } else { showPermissionDeniedToast(); } });2. 相机拍照功能实现2.1 启动系统相机调用系统相机拍照的核心代码如下private Uri imageUri; // 用于保存照片URI private void startCamera() { Intent takePictureIntent new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 创建临时文件 File photoFile createImageFile(); if (photoFile ! null) { imageUri FileProvider.getUriForFile( this, com.example.myapp.fileprovider, photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } }这里有几个关键点需要注意从Android 7.0开始直接使用file:// URI会抛出FileUriExposedException必须使用FileProvider照片保存路径应该使用getExternalFilesDir()这样卸载应用时会自动清理不同厂商手机可能有不同的相机实现需要处理各种兼容性问题2.2 处理拍照结果在onActivityResult中处理返回的照片Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode REQUEST_IMAGE_CAPTURE resultCode RESULT_OK) { try { // 压缩图片避免OOM Bitmap bitmap decodeSampledBitmapFromUri(imageUri, 1000, 1000); imageView.setImageBitmap(bitmap); // 通知系统相册更新 notifyMediaStore(imageUri); } catch (Exception e) { e.printStackTrace(); } } }图片压缩是个很重要的优化点特别是处理高分辨率相机拍摄的照片private Bitmap decodeSampledBitmapFromUri(Uri uri, int reqWidth, int reqHeight) { // 先获取图片尺寸 BitmapFactory.Options options new BitmapFactory.Options(); options.inJustDecodeBounds true; BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options); // 计算采样率 options.inSampleSize calculateInSampleSize(options, reqWidth, reqHeight); // 解码图片 options.inJustDecodeBounds false; return BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options); }3. 相册图片选择实现3.1 启动系统相册从相册选择图片的代码相对简单private void openAlbum() { Intent intent new Intent(Intent.ACTION_PICK); intent.setType(image/*); startActivityForResult(intent, REQUEST_IMAGE_PICK); }但在实际项目中我们可能需要更精细的控制只显示图片不显示视频限制选择的图片数量支持多选Android 13引入了新的照片选择器API提供了更好的用户体验// 单选模式 ActivityResultLauncherPickVisualMediaRequest pickMedia registerForActivityResult(new PickVisualMedia(), uri - { if (uri ! null) { handleSelectedImage(uri); } }); pickMedia.launch(new PickVisualMediaRequest.Builder() .setMediaType(PickVisualMedia.ImageOnly.INSTANCE) .build());3.2 处理不同Android版本的差异处理相册选择结果时需要特别注意不同Android版本的差异Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode REQUEST_IMAGE_PICK resultCode RESULT_OK) { Uri uri data.getData(); if (Build.VERSION.SDK_INT Build.VERSION_CODES.KITKAT) { handleImageOnKitKat(uri); } else { handleImageBeforeKitKat(uri); } } } TargetApi(19) private void handleImageOnKitKat(Uri uri) { String imagePath null; if (DocumentsContract.isDocumentUri(this, uri)) { String docId DocumentsContract.getDocumentId(uri); if (com.android.providers.media.documents.equals(uri.getAuthority())) { String id docId.split(:)[1]; String selection MediaStore.Images.Media._ID id; imagePath getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } } else if (content.equalsIgnoreCase(uri.getScheme())) { imagePath getImagePath(uri, null); } displayImage(imagePath); }4. 图片保存与优化4.1 图片保存到本地将图片保存到本地的标准做法public void saveImageToGallery(Bitmap bitmap) { // 创建保存目录 File dir new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), MyApp); if (!dir.exists()) { dir.mkdirs(); } // 创建文件 String fileName System.currentTimeMillis() .jpg; File file new File(dir, fileName); // 保存图片 try (FileOutputStream fos new FileOutputStream(file)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); // 通知系统相册更新 MediaStore.Images.Media.insertImage( getContentResolver(), file.getAbsolutePath(), fileName, null); // 发送广播刷新相册 sendBroadcast(new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); } catch (Exception e) { e.printStackTrace(); } }4.2 性能优化建议在实际项目中图片处理还需要考虑以下优化点内存优化使用inSampleSize减少内存占用异步加载使用线程池或协程处理耗时操作缓存策略实现内存和磁盘二级缓存图片压缩根据显示尺寸压缩图片生命周期管理避免内存泄漏一个简单的图片加载器实现public class ImageLoader { private ExecutorService executor Executors.newFixedThreadPool(4); private LruCacheString, Bitmap memoryCache; public ImageLoader() { // 初始化内存缓存 final int maxMemory (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize maxMemory / 8; memoryCache new LruCacheString, Bitmap(cacheSize) { Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount() / 1024; } }; } public void loadImage(String path, ImageView imageView) { // 先从内存缓存读取 Bitmap bitmap memoryCache.get(path); if (bitmap ! null) { imageView.setImageBitmap(bitmap); return; } // 异步加载 executor.execute(() - { Bitmap loadedBitmap decodeSampledBitmapFromFile(path, imageView.getWidth(), imageView.getHeight()); memoryCache.put(path, loadedBitmap); // 更新UI imageView.post(() - { imageView.setImageBitmap(loadedBitmap); }); }); } }5. 常见问题与解决方案5.1 权限相关问题问题在Android 10及以上版本即使申请了存储权限仍然无法访问某些文件。解决方案使用MediaStore API替代直接文件访问对于应用专属文件使用getExternalFilesDir()对于共享文件使用存储访问框架(SAF)5.2 相机方向问题问题某些手机拍摄的照片在ImageView中显示方向不正确。解决方案public static Bitmap rotateImageIfRequired(Bitmap bitmap, Uri uri) throws IOException { ExifInterface exif new ExifInterface(getContentResolver().openInputStream(uri)); int orientation exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: return rotateBitmap(bitmap, 90); case ExifInterface.ORIENTATION_ROTATE_180: return rotateBitmap(bitmap, 180); case ExifInterface.ORIENTATION_ROTATE_270: return rotateBitmap(bitmap, 270); default: return bitmap; } }5.3 大图加载OOM问题问题加载高分辨率图片时容易导致内存溢出。解决方案使用inSampleSize进行下采样使用BitmapRegionDecoder加载图片区域考虑使用第三方库如Glide或Picasso6. 现代Android开发的最佳实践随着Android开发的演进现在推荐使用以下现代技术实现相机和相册功能CameraXGoogle推荐的相机开发库简化了相机实现PhotoPickerAndroid 13引入的新API提供统一的图片选择界面Coil基于Kotlin协程的图片加载库Activity Result API简化权限请求和Activity结果处理CameraX的基本使用示例// 创建CameraProviderFuture ListenableFutureProcessCameraProvider cameraProviderFuture ProcessCameraProvider.getInstance(this); cameraProviderFuture.addListener(() - { try { // 绑定生命周期 ProcessCameraProvider cameraProvider cameraProviderFuture.get(); Preview preview new Preview.Builder().build(); ImageCapture imageCapture new ImageCapture.Builder().build(); CameraSelector cameraSelector new CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build(); cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture); // 设置预览Surface preview.setSurfaceProvider( previewView.getSurfaceProvider()); } catch (Exception e) { e.printStackTrace(); } }, ContextCompat.getMainExecutor(this));在项目开发中我发现合理组织代码结构非常重要。建议将相机和相册功能封装成独立的模块通过接口暴露必要功能这样既便于测试也方便后续维护和功能扩展。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询