Android 自定义Camera SurfaceView 拍照自动旋转图片及保存

前言:抱着最起码的要求尽力去做好每一件事 ! ——秋不白

本文适用针对项目的相机多张拍摄,查看图库(自己写的,查看自己拍摄的照片,不包含系统图库和其他文件夹的图片),其实也是自己项目中不断总结,为什么还要写个自定义相机呢,因为之前项目中写的不够好,拍照图片的旋转啊,现在离开公司了,总觉得还欠缺点什么,如有需要可以联系我,代码还没有上传github(后面我再放到gitbug),有兴趣,请移步https://gitee.com/redrose/Demo,查看完整代码。

我定义了Photo和classPhoto两个java bean,快速开发就接入了LitePal,Glide,鸿洋大神的适配器,

图库效果截图:

相机预览界面

功能:重力传感器监听手机横着竖着,(比如竖着拍摄的照片,查看的时候希望是横着的,而不是竖着),动态修改相机参数,保存照片,以及加载图片。

申请权限,权限要自己加了

前提:

1 自定义SurfaceView(还有GLSurfaceView,TextureView,使用SurfaceView,最简单,毕竟是Google亲儿子,都封装好了)

2.Camera 工具类,(单利模式管理Camera对象,参数设置,释放资源等)(封成工具类,方便后面自己用GLSurfaceView,TextureView去实现相机,以及后面接入openGL,,或者opencv 做人脸识别,识别人脸拍照等等)

Camera工具类

package com.redrose.videodemo.camera;

import android.graphics.ImageFormat;
import android.hardware.Camera;

import java.util.List;

/**
 * Desc: 相机工具类
 * author: RedRose
 * Date: 2019/3/20
 * Email: yinsxi@163.com
 */

public class CameraUtil {
    private static CameraUtil mInstance;
    /**
     * 相机参数对象
     */
    private Camera.Parameters mParameters;
    /**
     * 闪光灯自动
     */
    public static final int FLASH_AUTO = 0;
    /**
     * 闪光灯关闭
     */
    public static final int FLASH_OFF = 1;
    /**
     * 闪光灯开启
     */
    public static final int FLASH_ON = 2;

    private CameraUtil() {
    }

    private static final Object o = new Object();

    public static CameraUtil getInstance() {
        if (mInstance == null) {
            synchronized (o) {
                if (mInstance == null) {
                    mInstance = new CameraUtil();
                }
            }
        }
        return mInstance;
    }

    private Camera mCamera;

    public Camera openCamera() {
        // 0 表示开启后置相机
        return openCamera(0);
    }

    public Camera openCamera(int id) {
        if (mCamera == null) {
            mCamera = Camera.open(id);
        }
        setProperty();
        return mCamera;
    }

    /**
     * 相机属性设置
     */
    private void setProperty() {
        //设置相机预览页面旋转90°,(默认是横屏)
        mCamera.setDisplayOrientation(90);
        mParameters = mCamera.getParameters();
        //设置将保存的图片旋转90°(竖着拍摄的时候)
        mParameters.setRotation(90);
        mParameters.setPreviewSize(1920, 1080);
        mParameters.setPictureSize(1920, 1080);
//        mParameters.setPictureSize(4608, 3456);
        mParameters.setPictureFormat(ImageFormat.JPEG);
        mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
//        mParameters.set(ImageFormat.YUV_444_888);
        mCamera.setParameters(mParameters);
    }
    /**
     * 选装图片的角度
     */
    public void setRotateDegree(int degree) {
//        boolean result = degree == 0 || degree == 90
//        mParameters.setRotation(90);
        if (mCamera != null) {
            mParameters = mCamera.getParameters();
            mParameters.setRotation(degree);
            mCamera.setParameters(mParameters);
        }

    }

    /**
     * 获取支持的预览分辨率
     */
    public List<Camera.Size> getPreviewSizeList() {
        if (mCamera == null) {
            throw new NullPointerException("Camera can not be null");
        }
        return mCamera.getParameters().getSupportedPreviewSizes();
    }

    /**
     * 获取保存图片支持的分辨率
     */
    public List<Camera.Size> getPictureSizeList() {
        if (mCamera == null) {
            throw new NullPointerException("Camera can not be null");
        }
        return mCamera.getParameters().getSupportedPictureSizes();
    }

    /**
     * 设置闪光灯模式
     */
    public void setFlashMode(int mode) {
        mParameters = mCamera.getParameters();
        String flashMode = mParameters.getFlashMode();
        switch (mode) {
            case FLASH_AUTO:
                mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
                break;
            case FLASH_OFF:
                mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                break;
            case FLASH_ON:
                mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
                break;
            default:
                break;
        }
        mCamera.setParameters(mParameters);
    }

    /**
     * 释放相机资源
     */
    public void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.lock();
            mCamera.release();
            mCamera = null;
            isRelease = true;
        }
    }
    /**
     * 是否旋转图片 true 选装
     */
    private boolean isRelease = false;

    public boolean getIsRelease() {
        return isRelease;
    }
    /**
     * 设置保存图片的分辨率
     */
    public void setSaveSize(Camera.Size saveSize) {
        mParameters.setPictureSize(saveSize.width, saveSize.height);
        mCamera.setParameters(mParameters);
    }
}

自定义SurfaceView

package com.redrose.videodemo.camera;

import android.content.Context;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.redrose.videodemo.utils.BitmapCallBack;
import com.redrose.videodemo.utils.LogUtils;
import com.redrose.videodemo.utils.ToastUtil;
import com.redrose.videodemo.utils.ToolUtils;

import java.io.IOException;

/**
 * Desc:
 * Author: RedRose
 * Date: 2019/3/20
 * Email: yinsxi@163.com
 */

public class Preview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback, Camera.PictureCallback {

    private Camera mCamera;
    private BitmapCallBack mBitmapCallback;
    private static final String TAG = Preview.class.getSimpleName();

    public Preview(Context context) {
        this(context, null);
    }

    public Preview(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Preview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mCamera = CameraUtil.getInstance().openCamera();
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        LogUtils.e(TAG, " --surfaceCreated--");
        try {
            //这里必须要这样处理,比如锁屏,回到桌面,再次回到相机的时候,surfaceCreated 会重新执行
            //所以,要加个标识,判断是否被释放过资源
            if (CameraUtil.getInstance().getIsRelease()) {
                mCamera = CameraUtil.getInstance().openCamera();
            }
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        mCamera.setPreviewCallback(this);
        mCamera.startPreview();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        LogUtils.e(TAG, " --surfaceChanged--");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        LogUtils.e(TAG, " --surfaceDestroyed--");
        //每次 执行surfaceDestroyed,都要销毁,释放camera
        CameraUtil.getInstance().releaseCamera();
    }
    /**
     * 预览回调
     */
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {

    }
    /**
     * 拍摄成功后回调
     */
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        if (mBitmapCallback != null) {
            mBitmapCallback.backByte(data);
        }
        camera.startPreview();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /**
         * 触摸聚焦
         */
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    ToastUtil.show(getContext(), "聚焦成功");
                    camera.cancelAutoFocus();
                }
            }
        });
        return super.onTouchEvent(event);
    }

    public void takePhoto() {
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                //设置聚焦成功后再拍照,其实可以不用。看需求吧,可以直接调用takePicture()
                //有些手机会聚焦失败,也就是success是false
                if (success) {
                    ToastUtil.show(getContext(), "聚焦成功");
                    camera.cancelAutoFocus();
                    mCamera.takePicture(null, null, Preview.this);
                }
            }
        });
    }
    /**
     * 设置回调到Activity吧
     */
    public void setBitmapCallback(BitmapCallBack callback) {
        this.mBitmapCallback = callback;
    }
}
public interface BitmapCallBack {
    void backByte(byte[] data);
}

CamreaActivity

package com.redrose.videodemo.camera;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;

import com.redrose.videodemo.base.BaseActivity;

import org.litepal.crud.DataSupport;
import org.xutils.view.annotation.ContentView;
import org.xutils.view.annotation.Event;
import org.xutils.view.annotation.ViewInject;

import com.redrose.videodemo.R;
import com.redrose.videodemo.bean.Photo;
import com.redrose.videodemo.bean.Time;
import com.redrose.videodemo.utils.BitmapCallBack;
import com.redrose.videodemo.utils.GlideUtils;
import com.redrose.videodemo.utils.IntentUtils;
import com.redrose.videodemo.utils.LogUtils;
import com.redrose.videodemo.utils.MySensorHelper;
import com.redrose.videodemo.utils.SPUtils;
import com.redrose.videodemo.utils.ToastUtil;
import com.redrose.videodemo.utils.ToolUtils;
import com.zhy.adapter.recyclerview.CommonAdapter;
import com.zhy.adapter.recyclerview.base.ViewHolder;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * Desc:
 * Author: RedRose
 * Date: 2019/3/20
 * Email: yinsxi@163.com
 */
@ContentView(R.layout.activity_camera)
public class CameraActivity extends BaseActivity implements BitmapCallBack {
    @ViewInject(R.id.pre_text)
    private TextView mPreSize;
    @ViewInject(R.id.save_text)
    private TextView mSaveSize;
    @ViewInject(R.id.flashlight)
    private ImageView mFlashView;
    @ViewInject(R.id.photo_list)
    private ImageView mPreImageView;
    @ViewInject(R.id.take_photo)
    private View mTakePhoto;
    @ViewInject(R.id.camera_preview)
    private Preview mPreview;
    private boolean showLocation = false;
    private PopupWindow mPopWindow;
    private List<Photo> mPhotoList;
    private Time mTime;
    /**
     * 是否旋转图片
     * true 表示当前Activity 处于横屏,false:当前处于竖屏
     */
    private boolean mIsRotate = false;
    /**
     * 重力传感器监听
     * 横着 竖着状态
     */
    private MySensorHelper mMySensor;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initData();
    }

    private void initData() {
        mMySensor = new MySensorHelper(this);
        mMySensor.enable();
        int mode = SPUtils.getInt("Flash_mode");
        initFlashMode(mode);
        mPreview.setBitmapCallback(this);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        String format = sdf.format(calendar.getTime());
        mTime = new Time();
        mTime.setClassId(Long.parseLong(format));
        mTime.setDay(calendar.get(Calendar.DAY_OF_MONTH));
        mTime.setYear(calendar.get(Calendar.YEAR));
        mTime.setMonth(calendar.get(Calendar.MONTH) + 1);
        mPhotoList = new ArrayList<>();
    }

    private void showPopWindow(View view) {
        mPopWindow = new PopupWindow(view, ToolUtils.dp2px(mContext.getApplicationContext(),
                200), ToolUtils.dp2px(mContext.getApplicationContext(), 200));
        mPopWindow.setFocusable(true);
//        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#3F51B5")));
        mPopWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        mPopWindow.setOutsideTouchable(true);
        mPopWindow.update();
        if (showLocation) {
            mPopWindow.showAsDropDown(mSaveSize);
        } else {
            mPopWindow.showAsDropDown(mPreSize);
        }
    }

    /**
     * 显示支持保存的分辨率
     */
    private void showSupportSize(List<Camera.Size> list) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View view = inflater.inflate(R.layout.pop_size_layout, null);
        RecyclerView recycleView = view.findViewById(R.id.recycle_size);
        initRecycle(recycleView);
        SizeAdapter mAdapter = new SizeAdapter(mContext, R.layout.size_item, list);
        recycleView.setAdapter(mAdapter);
        showPopWindow(view);
    }

    @Event(value = {R.id.pre_text, R.id.save_text, R.id.flashlight, R.id.take_photo, R.id.photo_list,
            R.id.all_list})
    private void onclickView(View view) {
        int id = view.getId();
        switch (id) {
            /*暂时注释 动态设置预览分辨率*/
            case R.id.pre_text:
                /*预览分辨率,建议设置成屏幕分辨率,经测试,支持的最高就是屏幕分辨率 */
                showLocation = false;
                showSupportSize(CameraUtil.getInstance().getPreviewSizeList());
                break;
            //设置保存图片的分辨率
            case R.id.save_text:
                showLocation = true;
                showSupportSize(CameraUtil.getInstance().getPictureSizeList());
                break;
            //切换闪光灯模式
            case R.id.flashlight:
                int mode = SPUtils.getInt("Flash_mode");
                if (mode == CameraUtil.FLASH_AUTO) {
                    mode = CameraUtil.FLASH_OFF;
                } else if (mode == CameraUtil.FLASH_OFF) {
                    mode = CameraUtil.FLASH_ON;
                } else if (mode == CameraUtil.FLASH_ON) {
                    mode = CameraUtil.FLASH_AUTO;
                }
                SPUtils.put("Flash_mode", mode);
                initFlashMode(mode);
                break;
            //拍照
            case R.id.take_photo:
                mPreview.takePhoto();
                break;
            //打开当前拍摄的照片图库
            case R.id.photo_list:
                int size = mPhotoList.size();
                if (size == 0) {
                    ToastUtil.show(mContext, "还未拍摄照片,可以点击右侧查看所有照片");
                    return;
                }
                Bundle bundle = new Bundle();
                bundle.putSerializable("photoList", (ArrayList<Photo>) mPhotoList);
                IntentUtils.goToPreviewActivity(mContext, bundle);
                break;
            //打开图库
            case R.id.all_list:
                IntentUtils.goToImageActivity(mContext, null);
                break;
            default:
                break;
        }
    }

    private void initRecycle(RecyclerView recyclerView) {
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //设置布局管理器
        recyclerView.setLayoutManager(layoutManager);
        //设置为垂直布局,这也是默认的
        layoutManager.setOrientation(OrientationHelper.VERTICAL);
        //设置Adapter
//        recyclerView.setAdapter(recycleAdapter);
        //设置分隔线
//        recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
        //设置增加或删除条目的动画
        recyclerView.setItemAnimator(new DefaultItemAnimator());
    }

    @Override
    public void backByte(byte[] data) {
        GlideUtils.loadByte(mPreImageView, data);
        ToolUtils.saveImageFile(data, mPhotoList, mIsRotate);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar calendar = Calendar.getInstance();
        String format = sdf.format(calendar.getTime());
        List<Time> times = DataSupport.where("classId = ?", format).find(Time.class);
        mTime.setPhotoList(mPhotoList);
        if (times.size() == 0) {
            mTime.save();
        }
    }

    /**
     * Desc: 相机页面,拍摄照片保存分辨率 显示适配器
     * author: RedRose
     * Date: 2019/4/1
     * Email: yinsxi@163.com
     */
    private class SizeAdapter extends CommonAdapter<Camera.Size> {

        private SizeAdapter(Context context, int layoutId, List<Camera.Size> datas) {
            super(context, layoutId, datas);
        }

        @Override
        protected void convert(ViewHolder holder, Camera.Size size, int position) {
            LogUtils.d("----redrose 装载数据测试 ----");
            TextView textView = holder.getView(R.id.size_text);
            String sizeString = String.format("%d * % d", size.width, size.height);
            textView.setText(sizeString);
            if (showLocation) {
                textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        CameraUtil.getInstance().setSaveSize(size);
                        if (mPopWindow != null) {
                            mPopWindow.dismiss();
                            mPopWindow = null;
                        }
                        ToastUtil.show(mContext, size.width + "x" + size.height);
                    }
                });
            }
        }
    }

    private void initFlashMode(int mode) {
        if (mode == CameraUtil.FLASH_AUTO) {
            mFlashView.setImageResource(R.mipmap.flash_auto);
        } else if (mode == CameraUtil.FLASH_OFF) {
            mFlashView.setImageResource(R.mipmap.flash_off);
        } else if (mode == CameraUtil.FLASH_ON) {
            mFlashView.setImageResource(R.mipmap.flash_on);
        }
        CameraUtil.getInstance().setFlashMode(mode);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMySensor.disable();
        LogUtils.e(TAG, " ---onDestroy---");
    }
    /**
     * 是否旋转图片
     */
    public void setIsRotate(boolean isRotate) {
        if(mIsRotate == isRotate){
            return;
        }
        this.mIsRotate = isRotate;
        if (mIsRotate) {
            CameraUtil.getInstance().setRotateDegree(0);
        }else {
            CameraUtil.getInstance().setRotateDegree(90);
        }
    }
    public boolean getIsRotate(){
        return mIsRotate;
    }
}

重力监听传感器,判断手机是横着还是竖着

package com.redrose.videodemo.utils;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.util.Log;
import android.view.OrientationEventListener;

import com.redrose.videodemo.camera.CameraActivity;

import java.lang.ref.WeakReference;

/**
 * Desc:
 * 手机横着竖着监听
 *
 * @author: RedRose
 * Date: 2019/4/2
 * Email: yinsxi@163.com
 */

public class MySensorHelper {
    private static final String TAG = MySensorHelper.class.getSimpleName();
    private OrientationEventListener mLandOrientationListener;
    private OrientationEventListener mPortOrientationListener;
    private WeakReference<CameraActivity> mActivityWeakRef;
    private boolean isPortLock = false;
    private boolean isLandLock = false;

    public MySensorHelper(final CameraActivity activity) {
        this.mActivityWeakRef = new WeakReference(activity);
        this.mLandOrientationListener = new OrientationEventListener(activity, 3) {
            public void onOrientationChanged(int orientation) {
                LogUtils.d(MySensorHelper.TAG, "mLandOrientationListener");
                if (orientation < 100 && orientation > 80 || orientation < 280 && orientation > 260) {
                    LogUtils.e(MySensorHelper.TAG, "转到了横屏");
                    if (!MySensorHelper.this.isLandLock) {
                        CameraActivity mActivity = MySensorHelper.this.mActivityWeakRef.get();
                        if (mActivity != null) {
                            LogUtils.e(MySensorHelper.TAG, "转到了横屏##################");
                            /*mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                            isLandLock=true;
                            isPortLock=false;*/
                            if (!mActivity.getIsRotate()) {

                                mActivity.setIsRotate(true);
                            }
                        }
                    }
                }

            }
        };
        this.mPortOrientationListener = new OrientationEventListener(activity, 3) {
            public void onOrientationChanged(int orientation) {
                LogUtils.d(MySensorHelper.TAG, "mPortOrientationListener");
                if (orientation < 10 || orientation > 350 || orientation < 190 && orientation > 170) {
                    LogUtils.e(MySensorHelper.TAG, "转到了竖屏");
                    if (!MySensorHelper.this.isPortLock) {
                        CameraActivity mActivity = MySensorHelper.this.mActivityWeakRef.get();
                        if (mActivity != null) {
                            LogUtils.e(MySensorHelper.TAG, "转到了竖屏!!!!!!!!!!!!!!!!!!!!!!");
                            /*mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                            isPortLock=true;
                            isLandLock=false*/
                            ;
                            if (mActivity.getIsRotate()) {
                                mActivity.setIsRotate(false);
                            }
                        }
                    }
                }

            }
        };
        //this.disable();
    }

    //禁用切换屏幕的开关
    public void disable() {
        Log.e(TAG, "disable");
        this.mPortOrientationListener.disable();
        this.mLandOrientationListener.disable();
    }

    //开启横竖屏切换的开关
    public void enable() {
        this.mPortOrientationListener.enable();
        this.mLandOrientationListener.enable();
    }

    //设置竖屏是否上锁,true锁定屏幕,fanle解锁
    public void setPortLock(boolean lockFlag) {
        this.isPortLock = lockFlag;
    }

    //设置横屏是否锁定,true锁定,false解锁
    public void setLandLock(boolean isLandLock) {
        this.isLandLock = isLandLock;
    }
}

依赖的jar

implementation 'com.android.support:recyclerview-v7:26+'
implementation 'org.xutils:xutils:3.3.36'
implementation 'com.zhy:base-adapter:3.0.3'
implementation 'com.zhy:base-rvadapter:3.0.3'

CameraActivity 布局xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.redrose.videodemo.camera.Preview
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <TextView
        android:visibility="gone"
        android:id="@+id/pre_text"
        style="@style/camera_button_style"
        android:text="预览分辨率" />
    <TextView
        android:layout_marginEnd="10dp"
        android:layout_alignParentEnd="true"
        android:id="@+id/save_text"
        style="@style/camera_button_style"
        android:text="保存分辨率" />

    <ImageView
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:src="@mipmap/flash_on"
        android:id="@+id/flashlight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <View
        android:id="@+id/take_photo"
        android:layout_marginBottom="20dp"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:background="@drawable/take_photo_selector"
        android:layout_width="56dp"
        android:layout_height="56dp"/>
    <ImageView
        android:layout_alignParentBottom="true"
        android:layout_marginStart="10dp"
        android:layout_marginBottom="10dp"
        android:src="@mipmap/ic_launcher"
        android:id="@+id/photo_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:layout_marginEnd="10dp"
        android:layout_marginBottom="10dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:src="@mipmap/ic_launcher"
        android:id="@+id/all_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

共享参数工具类

/*
 *
 *   Copyright 2016 YunDi
 *
 *   The code is part of Yunnan, Shenzhen Branch of the internal architecture of YunDi source group
 *
 *   DO NOT DIVULGE
 *
 */

package com.redrose.videodemo.utils;


import android.content.Context;
import android.content.SharedPreferences;

import org.xutils.common.util.LogUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;


public class SPUtils {

    public static final String TAG = SPUtils.class.getSimpleName();

    public static final String KEY_USER_INFO = "user_info";

    private static Context mContext;

    public static void init(Context context) {
        mContext = context;
    }

    /**
     * 保存在手机里面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     *
     * @param key
     * @param object
     */
    public static void put(String key, Object object) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        if (object instanceof String) {
            editor.putString(key, (String) object);
        } else if (object instanceof Integer) {
            editor.putInt(key, (Integer) object);
        } else if (object instanceof Boolean) {
            editor.putBoolean(key, (Boolean) object);
        } else if (object instanceof Float) {
            editor.putFloat(key, (Float) object);
        } else if (object instanceof Long) {
            editor.putLong(key, (Long) object);
        } else {
            editor.putString(key, object.toString());
        }
        editor.commit();
    }

    public static String getString(String key) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.getString(key, null);
    }

    public static int getInt(String key) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.getInt(key, 0);
    }

    public static long getLong(String key) {
        return (long) get(key, 0);
    }

    public static float getFloat(String key) {
        return (float) get(key, 0f);
    }


    public static boolean getBoolean(String key) {
        return (boolean) get(key, false);
    }


    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     *
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object get(String key, Object defaultObject) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);

        if (defaultObject instanceof Integer) {
            return sp.getInt(key, (Integer) defaultObject);
        } else if (defaultObject instanceof Boolean) {
            return sp.getBoolean(key, (Boolean) defaultObject);
        } else if (defaultObject instanceof Float) {
            return sp.getFloat(key, (Float) defaultObject);
        } else if (defaultObject instanceof Long) {
            return sp.getLong(key, (Long) defaultObject);
        }

        return null;
    }

    /**
     * 移除某个key值已经对应的值
     *
     * @param key
     */
    public static void remove(String key) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 清除所有数据
     */
    public static void clear() {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 查询某个key是否已经存在
     *
     * @param key
     * @return
     */
    public static boolean contains(String key) {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的键值对
     *
     * @return
     */
    public static Map<String, ?> getAll() {
        SharedPreferences sp = mContext.getSharedPreferences(FILE_NAME,
                Context.MODE_PRIVATE);
        return sp.getAll();
    }


    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     */
    private static class SharedPreferencesCompat {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         *
         * @return
         */
        @SuppressWarnings({"unchecked", "rawtypes"})
        private static Method findApplyMethod() {
            try {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e) {
            }

            return null;
        }

        /**
         * 如果找到则使用apply执行,否则使用commit
         *
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor) {
            try {
                if (sApplyMethod != null) {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
            editor.commit();
        }
    }

}

有的同学实现这个功能后,在打开系统图库的时候,看不见这张图片,是因为系统每次开机时会去检索图片等资源,把索引存到数据库,所以要通知刷新媒体库,要发广播通知系统刷新(如何发,百度一大把,我就不赘述了)。

先就这么多吧,最关键的就是自定义SurfaceView和CameraUtil 工具类。可以接着后面,继续写,实现更多的需求,我的目标是使用MediaCodeC录制编码小视频和解码,这部分还未完成。改天推到gitbub,就可以拉完整代码了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1 安装AndroidSDK与ADTplug-in 2.2 建立第一个Android项目(HelloAndroid!) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色常数的方法 3.3 更改TextView文字颜色——引用Drawable颜色常数及背景色 3.4 置换TextView文字——CharSequence数据类型与ResourceID应用 3.5 取得手机屏幕大小——DisplayMetrics取得画面宽高的方法 3.6 样式化的定型对象——Style样式的定义 3.7 简易的按钮事件——Button事件处理 3.8 手机页面的转换——setContentView的应用 3.9 调用另一个Activity——Intent对象的使用 3.10 不同Activity之间的数据传递——Bundle对象的实现 3.11 返回数据到前一个Activity——startActivityForResult方法 3.12 具有交互功能的对话框——AlertDialog窗口 3.13 置换文字颜色的机关——Button与TextView的交互 3.14 控制不同的文字字体——Typeface对象使用 3.15 如iPhone拖动相片特效——Gallery画廊 3.16 自制计算器——多按钮的整合应用 3.17 关于(About)程序信息——Menu功能菜单程序设计 3.18 程序加载中,请稍后——ProgressDialog与线程整合应用 3.19 全屏幕以按钮覆盖——动态产生按钮并最大化 3.20 今晚到哪儿打牙祭?——具选择功能的对话框 3.21 Android变脸——主题(Theme)实现 第4章 史上超豪华的手机控件 4.1 EditText与TextView共舞——setOnKeyListener事件 4.2 设计具有背景图的按钮——ImageButton的焦点及事件处理 4.3 给耶诞老人的信息——Toast对象的使用 4.4 我同意条款——CheckBox的isChecked属性 4.5 消费券采购列表——多选项CheckBox的应用 4.6 向左或向右——RadioGroup组与onCheckedChanged事件 4.7 专业相框设计——ImageView的堆栈应用 4.8 自定义下拉菜单模式——Spinner与setDropDownViewResource 4.9 动态添加/删除的Spinner菜单——ArrayList与Widget的依赖性 4.10 心爱小宝贝相片集——Gallery与衍生BaseAdapter容器 4.11 快速的搜索手机文件引擎——JavaI/O的应用 4.12 按钮也能随点击变换——ImageButton选择特效 4.13 具自动提示功能的菜单——AutoCompleteTextView与数组 4.14 数字及模拟小时钟设计——AnalogClock与DigitalClock的原理 4.15 动态输入日期与时间——DatePicker与TimePicker应用 4.16 猜猜红心A在那儿——ImageView点击事件与透明度处理 4.17 后台程序运行进度提示——ProgressBar与Handler的整合应用 4.18 动态文字排版——GridView与ArrayAdapter设计 4.19 在Activity里显示列表列表——ListView的布局 4.20 以动态列表配置选项——ListActivity与Menu整合技巧 4.21 查找程序根目录下所有文件——JavaI/O与ListActivity的结合.. 4.22 加载手机磁盘里的图文件——使用decodeFile方法 4.23 动态放大缩小ImageView里的图片——运用Matrix对象来缩放图文件 4.24 动态旋转图片——Bitmap与Matrix旋转ImageView 4.25 猜猜我在想什么——RadioButtonID 4.26 离开与关闭程序的弹出窗口——对话窗口上的ICON图标 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的TextView——Linkify规则 5.2 ACTION!CALL!拨打电话——Intent.ACTION.CALL的使用 5.3 自制发送短信程序——SmsManager与PendingIntent对象 5.4 自制发送Email程序——Intent在Email上的使用 5.5 自制日历手机数据库——实现SQLiteOpenHelper 5.6 手机震动的节奏——Vibrator对象及周期运用 5.7 图文可视化提醒——Toast与LinearLayoutView 5.8 状态栏的图标与文字提醒——NotificationManager与Notification对象的应用 5.9 搜索手机通讯录自动完成——使用ContentResolver 5.10 取得联系人资料——Provider.Contact的使用 5.11 制作有图标的文件资源管理器——自定义Adapter对象 5.12 还原手机默认桌面——重写clearWallpaper方法 5.13 置换手机背景图——Gallery与setWallpaper整合实现 5.14 撷取手机现存桌面——getWallpaper与setImageDrawable 5.15 文件资源管理器再进化——JavaI/O修改文件名及删除 5.16 取得目前File与Cache的路径——getCacheDir与getFilesDir 5.17 打开/关闭WiFi服务——WifiManager状态判断 5.18 取得SIM卡内的信息——TelephonyManager的应用 5.19 调用拨号按钮——打电话CALL_BUTTON 5.20 DPAD按键处理——onKeyDown事件与Layout坐标交互 5.21 任务管理器正在运行的程序——RunningTaskInfo 5.22 动态更改屏幕方向——LANDSCAPE与PORTRAIT 5.23 系统设置更改事件——onConfigurationChanged信息处理 5.24 取得电信网络与手机相关信息——TelephonyManager与android.provider.Settings.System的应用 第6章 手机自动服务纪实 6.1 您有一条短信popup提醒——常驻BroadcastReceiver的应用 6.2 手机电池计量还剩多少——使用BroadcastReceiver捕捉Intent.ACTION_BATTERY_CHANGED 6.3 群发拜年短信给联系人——ACTION_PICK与Uri对象 6.4 开始与停止系统服务——Service与Runnable整合并用 6.5 通过短信发送email通知——BroadcastReceiver与Intent整合 6.6 手机拨接状态——PhoneStateListener之onCallStateChanged 6.7 有来电,发送邮件通知——PhoneStateListener与ACTION_SEND 6.8 存储卡剩余多少容量——Environment加StatFs 6.9 访问本机内存与存储卡——File的创建与删除 6.10 实现可定时响起的闹钟——PendingIntent与AlarmManager的运用 6.11 黑名单来电自动静音——PhoneStateListener与AudioManager 6.12 手机翻背面即静音震动——SensorListener及AudioManager整合应用 6.13 指定时间置换桌面背景——多AlarmManager事件处理 6.14 判断发送短信后的状态——BroadcastReceiver聆听PendingIntent 6.15 后台服务送出广播信息——sendBroadcast与BroadcastReceiver 6.16 开机程序设计——receiver与intent-filter协同作业 6.17 双向短信常驻服务——Service与receiver实例 第7章 娱乐多媒体 7.1 访问Drawable资源的宽高——ContextMenu与Bitmap的应用 7.2 绘制几何图形——使用android.graphics类 7.3 手机屏幕保护程序——FadeIn/FadeOut特效与运行线程 7.4 用手指移动画面里的照片——onTouchEvent事件判断 7.5 加载存储卡的Gallery相簿——FileArrayList 7.6 取得手机内置媒体里的图文件——ACTION_GET_CONTENT取回InputStream 7.7 相片导航向导与设置背景桌面——ImageSwitcher与Gallery 7.8 调整音量大小声——AudioManager控制音量 7.9 播放mp3资源文件——raw文件夹与MediaPlayer的使用 7.10 播放存储卡里的mp3音乐——MediaPlayer.setDataSource 7.11 自制录音/播放录音程序——MediaRecorder与AudioEncoder 7.12 通过收到短信开始秘密录音——MediaRecorder与BroadcastReceiver实例 7.13 内置影片播放器载入3gp电影——VideoViewWidget 7.14 自制3gp影片播放器——MediaPlayer与实现SurfaceView 7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——WebView.loadData 8.4 设计前往打开网页功能——Intent与Uri.parse 8.5 将网络图像网址放入Gallery中显示——URL.URLConnection.BaseAdapter 8.6 即时访问网络图文件展示——HttpURLConnection 8.7 手机气象局,实时卫星云图——HttpURLConnection与URLConnection和运行线程 8.8 通过网络播放MP3——Runnable存储FileOutputStream技巧 8.9 设置远程下载音乐为手机铃声——RingtoneManager与铃声存放路径 8.10 远程下载桌面背景图案——URLConnection与setWallpaper()搭配 8.11 将手机文件上传至网站服务器——模拟HTTPFORM的POSTACTION 8.12 移动博客发布器——以XML-RPC达成远程过程调用 8.13 移动RSS阅读器——利用SAXParser解析XML 8.14 远程下载安装Android程序——APKInstaller的应用 8.15 手机下载看3gp影片——Runnable混搭SurfaceView 8.16 访问网站LoginAPI——远程服务器验证程序运行权限 8.17 地震速报!——HttpURLConnection与Service侦测服务 第9章 Google服务与Android混搭 9.1 Google帐号验证Token——AuthSub 9.2 Google搜索——AutoCompleteTextView与GoogleSearchAPI 9.3 前端产生QRCode二维条形码——GoogleChartAPI 9.4 以经纬度查找目的地位置——GeoPoint与MapView的搭配运用 9.5 GPSGoogle地图——LocationListener与MapView实时更新 9.6 移动版GoogleMap——Geocoder反查Address对象 9.7 规划导航路径——DirectionsRoute 9.8 移动设备上的Picasa相册——GooglePicasaAPI 9.9 随身翻译机——GoogleTranslateAPI 第10章 创意Android程序设计 10.1 手机手电筒——PowerManager控制WakeLock并改变手机亮度 10.2 GPS轨迹记录器——利用LocationListener在地图上画图并换算距离 10.3 女性贴身看护——AlarmManager.DatePicker.TimePicker 10.4 手机QRCode二维条形码生成器——Canvas与SurfaceHolder绘图 10.5 AndroidQRCode二维条形码扫描仪——BitmapFactory.decodeByteArray 10.6 上班族今天中午要吃什么——热量骰子地图 10.7 掷杯筊——把手机放在空中甩事件处理...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值