Android BottomSheet:以选取图片为例(2)
附录文章5简单介绍了常见的分享面板在BottomSheet中的具体应用。本文再以常见的选取图片为例写一个例子。
布局文件:
<?xml version="1.0" encoding="utf-8"?> <com.flipboard.bottomsheet.BottomSheetLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/bottomsheet" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <Button android:id="@+id/image_picker_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|top" android:layout_marginBottom="16dp" android:text="选择" /> <ImageView android:id="@+id/image_picker_selected" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:layout_below="@id/image_picker_button" /> </RelativeLayout> </com.flipboard.bottomsheet.BottomSheetLayout>
上层Java代码:
package zhangphil.demo; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.flipboard.bottomsheet.BottomSheetLayout; import com.flipboard.bottomsheet.commons.ImagePickerSheetView; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class MainActivity extends AppCompatActivity { private static final int REQUEST_STORAGE = 0; private static final int REQUEST_IMAGE_CAPTURE = REQUEST_STORAGE + 1; private static final int REQUEST_LOAD_IMAGE = REQUEST_IMAGE_CAPTURE + 1; protected BottomSheetLayout bottomSheetLayout; private Uri cameraImageUri = null; private ImageView selectedImage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet); bottomSheetLayout.setPeekOnDismiss(true); selectedImage = (ImageView) findViewById(R.id.image_picker_selected); findViewById(R.id.image_picker_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (checkNeedsPermission()) { requestStoragePermission(); } else { showSheetView(); } } }); } private boolean checkNeedsPermission() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void requestStoragePermission() { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE); } else { // Eh, prompt anyway ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE); } } @TargetApi(Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_STORAGE) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { showSheetView(); } else { // Permission denied Toast.makeText(this, "Sheet is useless without access to external storage :/", Toast.LENGTH_SHORT).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } /** * Show an {@link ImagePickerSheetView} */ private void showSheetView() { ImagePickerSheetView sheetView = new ImagePickerSheetView.Builder(this) .setMaxItems(30) .setShowCameraOption(createCameraIntent() != null) .setShowPickerOption(createPickIntent() != null) .setImageProvider(new ImagePickerSheetView.ImageProvider() { @Override public void onProvideImage(ImageView imageView, Uri imageUri, int size) { Glide.with(MainActivity.this) .load(imageUri) .centerCrop() .crossFade() .into(imageView); } }) .setOnTileSelectedListener(new ImagePickerSheetView.OnTileSelectedListener() { @Override public void onTileSelected(ImagePickerSheetView.ImagePickerTile selectedTile) { bottomSheetLayout.dismissSheet(); if (selectedTile.isCameraTile()) { dispatchTakePictureIntent(); } else if (selectedTile.isPickerTile()) { startActivityForResult(createPickIntent(), REQUEST_LOAD_IMAGE); } else if (selectedTile.isImageTile()) { showSelectedImage(selectedTile.getImageUri()); } else { genericError(); } } }) .setTitle("选择一张照片...") .create(); bottomSheetLayout.showWithSheetView(sheetView); } /** * For images captured from the camera, we need to create a File first to tell the camera * where to store the image. * * @return the File created for the image to be store under. */ private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File imageFile = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents cameraImageUri = Uri.fromFile(imageFile); return imageFile; } /** * This checks to see if there is a suitable activity to handle the `ACTION_PICK` intent * and returns it if found. {@link Intent#ACTION_PICK} is for picking an image from an external app. * * @return A prepared intent if found. */ @Nullable private Intent createPickIntent() { Intent picImageIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); if (picImageIntent.resolveActivity(getPackageManager()) != null) { return picImageIntent; } else { return null; } } /** * This checks to see if there is a suitable activity to handle the {@link MediaStore#ACTION_IMAGE_CAPTURE} * intent and returns it if found. {@link MediaStore#ACTION_IMAGE_CAPTURE} is for letting another app take * a picture from the camera and store it in a file that we specify. * * @return A prepared intent if found. */ @Nullable private Intent createCameraIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { return takePictureIntent; } else { return null; } } /** * This utility function combines the camera intent creation and image file creation, and * ultimately fires the intent. * * @see {@link #createCameraIntent()} * @see {@link #createImageFile()} */ private void dispatchTakePictureIntent() { Intent takePictureIntent = createCameraIntent(); // Ensure that there's a camera activity to handle the intent if (takePictureIntent != null) { // Create the File where the photo should go try { File imageFile = createImageFile(); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile)); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } catch (IOException e) { // Error occurred while creating the File genericError("Could not create imageFile for camera"); } } } @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK) { Uri selectedImage = null; if (requestCode == REQUEST_LOAD_IMAGE && data != null) { selectedImage = data.getData(); if (selectedImage == null) { genericError(); } } else if (requestCode == REQUEST_IMAGE_CAPTURE) { // Do something with imagePath selectedImage = cameraImageUri; } if (selectedImage != null) { showSelectedImage(selectedImage); } else { genericError(); } } } private void showSelectedImage(Uri selectedImageUri) { selectedImage.setImageDrawable(null); Glide.with(this) .load(selectedImageUri) .crossFade() .fitCenter() .into(selectedImage); } private void genericError() { genericError(null); } private void genericError(String message) { Toast.makeText(this, message == null ? "Something went wrong." : message, Toast.LENGTH_SHORT).show(); } }
由于需要读取设备图象文件,须增加权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
本例中在加载图片时候使用了Glide,关于Glide,请查阅附录文章4。
代码运行结果--->
初始化状态:
点击button按钮弹出选择面板:
拖曳向上铺满窗口:
本例中的照片选取代码是一个相对比较独立、可以单独抽取出去的通用模块化代码,支持从图库中选取,也支持调用相机拍照出图,在其他项目中使用,只需要关注诸如本例中的button按钮事件以及最终的图片加载位置,就可以在自己的项目灵活使用。
附录文章:
1,《Android自底部平滑向上滑出面板的AndroidSlidingUpPanel》链接地址:http://blog.csdn.net/zhangphil/article/details/51444509
2,《Android音乐、视频类APP常用控件:DraggablePanel(1)》链接地址:http://blog.csdn.net/zhangphil/article/details/51566860
3,《Android音乐、视频类APP常用控件:DraggablePanel(2)》链接地址:http://blog.csdn.net/zhangphil/article/details/51578665
4,《Android图片加载与缓存开源框架:Android Glide》链接地址http://blog.csdn.net/zhangphil/article/details/45535693 :
5,《Android BottomSheet:便捷易用的底部滑出面板(1)》链接地址:http://blog.csdn.net/zhangphil/article/details/51775955