11 使用intent拍照

引入布局文件

新建布局文件

view_camera_and_title.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_marginTop="16dp">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginRight="4dp">
        <ImageView
            android:id="@+id/crime_photo"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:scaleType="centerInside"
            android:background="@android:color/darker_gray"
            android:cropToPadding="true" />
        <ImageButton
            android:id="@+id/crime_camera"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_menu_camera" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="1">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/crime_title_label"
            style="?android:listSeparatorTextViewStyle" />
        <EditText
            android:id="@+id/crime_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginRight="16dp"
            android:hint="@string/crime_title_hint" />
    </LinearLayout>
</LinearLayout>

在这里插入图片描述

引入布局文件

fragment_crime.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/view_camera_and_title" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/crime_details_label"
        style="?android:listSeparatorTextViewStyle" />
    <!-- ... -->
</LinearLayout>

外部存储

指定照片存放位置

添加文件名获取方法

Crime.java

public String getPhotoFilename() {
    return "IMG_" + getId().toString() + ".jpg";
}

Crime.getPhotoFilename() 方法不知道图片文件该存储在哪个目录。CrimeLab 负责 CriminalIntent 应用的数据持久工作,那么在 CrimeLab 类里添加 getPhotoFile(Crime) 方法再合适不过。

添加定位文件保存目录方法

CrimeLab.java

public File getPhotoFile(Crime crime) {
    //获得存储图像文件的外部存储路径
    File externalFileDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);

    if (externalFileDir == null) return null;

    return new File(externalFileDir, crime.getPhotoFilename());
}

上述新增方法不会创建任何文件。它的作用就是返回指向某个具体位置的 File 对象。我们在这个方法里加了个校验:确认外部存储是否可用。如果不可用,getExternalFilesDir(String)方法会返回 null 值。


使用相机intent

获取图片文件路径

CrimeFragment.java

private File mPhotoFile;    //图像文件(带路径)
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
    mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
    //初始化图像文件路径
    mPhotoFile = CrimeLab.get(getActivity()).getPhotoFile(mCrime);
}

申请外部存储读写权限

既然 Context.getExternalFilesDir(String) 方法会返回应用专用的文件目录,那么可以直接读写这个目录应该是理所当然的事。因此,对Android 4.4 (API 19) 及其之后的新版系统来说,应用就不用申请使用这个目录的权限了,但使用其他外部存储仍然需要。

AndroidManifest.xml

...
	<!-- 只有在API级别小于19的设备上,应用才需要申请外部存储读权限 -->
    <uses-feature
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />
...

触发拍照

CrimeFragment.java

private static final int REQUEST_PHOTO = 4;
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    //...
    mPhotoButton = view.findViewById(R.id.crime_camera);

    final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //文件存在且intent能被合适的activity解析时才可以拍照
    boolean canTakePhoto = mPhotoFile != null && captureImage.resolveActivity(packageManager) != null;
    mPhotoButton.setEnabled(canTakePhoto);

    if (canTakePhoto) {
        Uri uri = Uri.fromFile(mPhotoFile);
        captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    }

    mPhotoButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivityForResult(captureImage, REQUEST_PHOTO);
        }
    });

    mPhotoView = view.findViewById(R.id.crime_photo);
    updatePhotoView();

    return view;
}

缩放和显示位图

缩放位图

手工缩放位图照片,首先确认文件到底有多大,然后考虑按照给定区域大小合理缩放文件。最后,重新读取缩放后的文件,创建 Bitmap对象。

PictureUtils.java

public class PictureUtils {
    public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;

        int inSampleSize = 1;
        if (srcHeight > destHeight || srcWidth > destWidth) {
            if (srcWidth > srcHeight) inSampleSize = Math.round(srcHeight / destHeight);
            else inSampleSize = Math.round(srcWidth / destWidth);
        }

        options = new BitmapFactory.Options();
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeFile(path, options);
    }
}

上述方法中,inSampleSize 值很关键。它决定着缩略图像素的大小。假设这个值是1的话,就表明缩略图和原始照片的水平像素大小一样。如果是2的话,它们的水平像素比就是1∶2。因此,inSampleSize 值为2时,缩略图的像素数就是原始文件的四分之一。

编写合理的缩放方法

fragment刚启动时,PhotoView究竟有多大无人知道。 onCreate(…) 、 onStart() 和 onResume() 方法启动后,才会有首个实例化布局出现。也就在此时,显示在屏幕上的视图才会有大小尺寸。

解决方案有两个:要么等布局实例化完成并显示,要么干脆使用保守估算值。特定条件下,
尽管估算比较主观,但确实是唯一切实可行的办法。首先确认屏幕的尺寸,然后按此缩放图像。这样,就能保证载入的ImageView永远不会过大。

PictureUtils.java

//确认屏幕尺寸,然后根据此比例缩放图像
public static Bitmap getScaledBitmap(String path, Activity activity) {
    Point size = new Point();
    activity.getWindowManager().getDefaultDisplay().getSize(size);

    return getScaledBitmap(path, size.x, size.y);
}

在ImageView显示图像

CrimeFragment.java

//若图像文件成功创建,就将图片显示在控件上
private void updatePhotoView() {
    if (mPhotoFile == null || !mPhotoFile.exists()) mPhotoView.setImageDrawable(null);
    else {
        Bitmap bitmap = PictureUtils.getScaledBitmap(mPhotoFile.getPath(), getActivity());
        mPhotoView.setImageBitmap(bitmap);
    }
}
在初始化和返回结果时更新图像

CrimeFragment.onCreateView(LayoutInflater, ViewGroup, Bundle)

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_crime, container, false);
    //...
    
    mPhotoView = view.findViewById(R.id.crime_photo);
    updatePhotoView();

    return view;
}

CrimeFragment.onActivityResult(int, int, Intent)

if (requestCode == REQUEST_PHOTO) {
    updatePhotoView();
}

实现点击缩略图显示照片

新建显示照片的布局

dialog_photo.xml

<ImageView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dialog_photo_image_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
</ImageView>

新建PhotoViewFragment类

PhotoViewFragment.java

public class PhotoViewFragment extends DialogFragment {
    private static final String ARG_FILE = "file";
    private ImageView mPhotoView;

    public static PhotoViewFragment newInstance(File file) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_FILE, file);

        PhotoViewFragment photoViewFragment = new PhotoViewFragment();
        photoViewFragment.setArguments(args);

        return photoViewFragment;
    }
    
    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        File photo = (File) getArguments().getSerializable(ARG_FILE);

        View view = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_photo, null);

        mPhotoView = view.findViewById(R.id.dialog_photo_image_view);

        //若图像文件成功创建,就将图片显示在控件上
        if (photo == null || !photo.exists()) mPhotoView.setImageDrawable(null);
        else {
            Bitmap bitmap = PictureUtils.getScaledBitmap(photo.getPath(), getActivity());
            mPhotoView.setImageBitmap(bitmap);
        }

        return new AlertDialog.Builder(getActivity())
                .setView(view)
                .create();
    }
}

设置单击事件监听器

CrimeFragment.onCreateView(LayoutInflater, ViewGroup, Bundle)

//...
mPhotoView = view.findViewById(R.id.crime_photo);
updatePhotoView();
mPhotoView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d(TAG, "点击了图片...");
        Log.d(TAG, "图片路径:" + mPhotoFile.getPath());

        FragmentManager manager = getFragmentManager();
        PhotoViewFragment dialog = PhotoViewFragment.newInstance(mPhotoFile);
        dialog.show(manager, DIALOG_PHOTO);
    }
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值