安卓干货——PC 连接手机实现摄像头预览

本文是一篇干货,我连配图都没放!

1 功能需求

(1)手机端打开摄像头实时预览;

(2)手机端作为服务端,PC端作为客户端连接;

(3)连接成功后PC端可以同时预览手机端的摄像头采集的图像;

(4)PC端点击拍照可以控制手机端拍摄一张照片,并将照片传给PC端。

2 功能模块

(1)安卓手机打开摄像头并实现预览和拍照功能;

(2)手机端开启监听,并在连接成功后将摄像头采集的数据传给PC;

(3)手机端读取PC发送的命令指令,执行相应的操作。

3 开发流程

3.1 开启摄像头实现预览

(1) 获取摄像头权限,并添加自动对焦属性。   在应用程序的manifest.xml文件中添加使用手机摄像头权限。由于程序需要使用socket的通信过程,将手机作为服务端,因此需要添加网络权限。

 <uses-permission android:name="android.permission.CAMERA" />
 <uses-feature android:name="android.hardware.camera" />  
 <uses-feature android:name="android.hardware.camera.autofocus" />
 <uses-permission Android:name="android.permission.INTERNET"/>
复制代码

2)实现相机的实时预览。    安卓系统使用SurfaceView即可完成预览功能。在布局文件中添加SurfaceView。activity_main.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:background="@color/gray_light"
android:orientation="vertical" >

<SurfaceView
    android:id="@+id/surview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scaleType="fitCenter" />

</LinearLayout>
复制代码

(3)在MainActivity 的onCreate中初始化SurfceView。初始化方法如下:

private void initSurfaceView() {
    surfaceView = (SurfaceView) findViewById(R.id.surview);
    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    surfaceHolder.setKeepScreenOn(true);
    surfaceHolder.addCallback(new Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder arg0) {

        }

        @Override
        public void surfaceCreated(SurfaceHolder arg0) {
            // 开启摄像头
            startCamera(curCameraIndex);
        }

        @Override
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
                int arg3) {

        }
    });
    surfaceView.setFocusable(true);
    surfaceView.setBackgroundColor(TRIM_MEMORY_BACKGROUND);

}
复制代码

4)SurfceView创建完成时调用开启摄像头的方法startCamera()。startCamera方法如下,其中代码含义已在注释程序中注明。

// 根据索引初始化摄像头
@SuppressLint("NewApi")
public void startCamera(int cameraIndex) {

    // 先停止摄像头
    stopCamera();
    // 再初始化并打开摄像头
    if (camera == null) {
        //打开手机摄像头,cameraIndex为摄像头索引,0代表后置摄像头,1代表前置摄像头。
        camera = Camera.open(cameraIndex);
        //创建摄像头操作工具类
        cameraUtil = new CameraUtil(camera, callback);
        //开启预览
        cameraUtil.initCamera(srcFrameHeight, srcFrameWidth, surfaceHolder);
        Log.e(TAG, "打开相机");
    }
}

// 停止并释放摄像头
public void stopCamera() {
    if (camera != null) {
        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
        camera = null;
    }
}

//摄像头开启预览后采集到的数据回调接口
PreviewCallback callback = new PreviewCallback() {

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Size size = camera.getParameters().getPreviewSize();
        try {
            if (times == 0) {
                YuvImage image = new YuvImage(data, ImageFormat.NV21,
                        size.width, size.height, null);
                if (image != null) {
                    // 将YuvImage对象转为字节数组
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    image.compressToJpeg(new Rect(0, 0, size.width,
                            size.height), 100, outputStream);
                    byte[] srcData = outputStream.toByteArray();
                    int len = srcData.length;

                    // 字节数组转为Bitmap
                    Bitmap src = BitmapFactory.decodeByteArray(srcData, 0,
                            len);
                    src = BitmapUtil.rotate(src, 90);
                    // 压缩Bitmap,并获取压缩后的字节数组,即可获取预览数据文件
                    // outdata数据即是待发送给PC端的数据
                    byte[] outdata = BitmapUtil.transImage(src,
                            srcFrameWidth / 4, srcFrameHeight / 4);
                    int datalen = outdata.length;

                    if (isOpen) {
                        // 写入头
                        sendData(new byte[] { (byte) 0xA0 });
                        // 写入数组长度
                        sendData(intToByteArray(datalen));
                        // 写入数据值
                        sendData(outdata);
                    }

                    // 回收Bitmap
                    if (!src.isRecycled()) {
                        src.recycle();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};
复制代码

(5)CameraUtil为设置摄像头的辅助类,代码如下:

public class CameraUtil {
    //摄像头类
    Camera camera;
    int cameraIndex;
    //预览视图宽和高
    int srcFrameWidth;
    int srcFrameHeight;
    SurfaceHolder surfaceHolder;
    PreviewCallback callback;

    public CameraUtil(Camera camera, PreviewCallback callback) {
        this.camera = camera;
        this.callback = callback;
       }

    //摄像头预览初始化
    public void initCamera(final int srcFrameWidth, final int srcFrameHeight, final SurfaceHolder surfaceHolder) {

        this.srcFrameHeight = srcFrameHeight;
        this.srcFrameWidth = srcFrameWidth;
        this.surfaceHolder = surfaceHolder;
        Camera.Parameters params = camera.getParameters();
        //params.setPreviewSize(srcFrameWidth / 4, srcFrameHeight / 4);
        //设置预览格式
        params.setPreviewFormat(ImageFormat.NV21);
        params.setPreviewFrameRate(30);
        //设置图像质量
        params.setJpegQuality(100);
        params.setPictureFormat(ImageFormat.JPEG);
        //设置预览方向
        params.set("orientation", "portrait");
        params.set("rotation", 90);
        //设置对焦模式为自动连续对焦
        params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
        camera.setParameters(params);
        camera.setDisplayOrientation(90);
        // 设置显示图像的SurfaceView
        try {
            camera.setPreviewDisplay(surfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        camera.setPreviewCallback(callback);
        //启动预览
        camera.startPreview();
        camera.autoFocus(new AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean result, Camera camera) {

            // 自动对焦完成时回调
            if (result) {
                    initCamera(srcFrameWidth, srcFrameHeight, surfaceHolder);
                    camera.cancelAutoFocus();
                }
            }
        });
    }

    @SuppressLint("NewApi") 
    public void startCamera(int cameraIndex) {

        this.cameraIndex = cameraIndex;
        // 先停止摄像头
        stopCamera();
        // 再初始化并打开摄像头
        if (camera == null) {
            camera = Camera.open(cameraIndex);
            initCamera(srcFrameWidth, srcFrameHeight, surfaceHolder);       
        }
    }

    //停止预览
    public void stopCamera() {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera = null;
        }
    }
}
复制代码

6)BitmapUtil为图片操作辅助类

public class BitmapUtil {

    // Bitmap按照一定大小转为字节数组,以便写入socket进行发送
    public static byte[] transImage(Bitmap bitmap, int width, int height) {
        // bitmap = adjustPhotoRotation(bitmap, 90);
        try {
            int bitmapWidth = bitmap.getWidth();
            int bitmapHeight = bitmap.getHeight();
            float scaleWidth = (float) width / bitmapWidth;
            float scaleHeight = (float) height / bitmapHeight;
            Matrix matrix = new Matrix();
            matrix.postScale(scaleWidth, scaleHeight);
            // 创建压缩后的Bitmap
            Bitmap resizeBitemp = Bitmap.createBitmap(bitmap, 0, 0,
                bitmapWidth, bitmapHeight, matrix, false);

            // 压缩图片质量
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            resizeBitemp.compress(CompressFormat.JPEG, 80, outputStream);
            // 转为字节数组
            byte[] byteArray = outputStream.toByteArray();
            outputStream.close();

            // 回收资源
            if (!bitmap.isRecycled()) {
                bitmap.recycle();
            }
            if (!resizeBitemp.isRecycled()) {
                resizeBitemp.recycle();
            }
            return byteArray;

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    //图片旋转操作
    public static Bitmap rotate(Bitmap bitmap, float degree) {
        Matrix matrix = new Matrix();
       // matrix.setScale(0.5f, 0.5f);// 缩小为原来的一半
        matrix.postRotate(degree);// 旋转45度 == matrix.setSinCos(0.5f, 0.5f);
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
        bitmap.getHeight(), matrix, true);
        return bitmap;
    }
}
复制代码

3.2 连接手机与PC

手机与PC的连接实际上是使用socket通信完成的。将手机端作为服务器开启监听。PC端发起连接,通过adb命令的端口转换将PC的连接请求发送至手机,建立手机与PC的连接。主要操作流程如下:

(1) onCreate中注册广播的监听 注册广播监听主要是接收PC端调用adb发送的广播信息,用于通知手机端开启监听和停止监听。

IntentFilter filter = new IntentFilter();
    filter.addAction("NotifyServiceStart");
    filter.addAction("NotifyServiceStop");
    registerReceiver(receiver, filter);
复制代码

(2) 接收系统广播

public class ServiceBroadcastReceiver extends BroadcastReceiver {

    private static final String START_ACTION = "NotifyServiceStart";
    private static final String STOP_ACTION = "NotifyServiceStop";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (START_ACTION.equalsIgnoreCase(action)) {
            // 启动服务
            Log.e(TAG, "收到广播信息启动监听");
            new Thread() {
                public void run() {

                    if (serverSocket != null) {
                        try {
                            serverSocket.close();
                        } catch (IOException e) {

                            e.printStackTrace();
                        }
                    }
                    //服务端启动监听
                    doListen();
                };
            }.start();

        } else if (STOP_ACTION.equalsIgnoreCase(action)) {

        }
    }
}
复制代码

(3)启动监听 手机端作为服务端,启动监听。注意对于网络的操作不能在主线程中。启动监听的方法doListen()如下:

// 启动服务器端监听
    private void doListen() {
        serverSocket = null;
        try {
            serverSocket = new ServerSocket(SERVER_PORT);
            while (true) {
                Socket socket = serverSocket.accept();
                Log.e(TAG, "监听到设备连接,启动通信线程");
                //PC端连接成功,启动通信线程ThreadReadWriterIOSocket
                threadSocket = new ThreadReadWriterIOSocket(socket);
                new Thread(threadSocket).start();
            }
        } catch (IOException e) {
            Log.e(TAG, "服务端监听失败");
            e.printStackTrace();
        }
    }
复制代码

4)ThreadReadWriterIOSocket为负责通信的线程。 ThreadReadWriterIOSocket是实现了Runnable接口的一个线程,其主要任务就是接收PC端发送的命令信息,并将命令信息通过EventBus框架发送至主界面。

public class ThreadReadWriterIOSocket implements Runnable {

    private static String TAG = "ThreadReadWriterIOSocket";
    private Socket client;
    private BufferedOutputStream out;
    private BufferedInputStream in;
    boolean isConnecting = false;
    private String cmd = "";

    public ThreadReadWriterIOSocket(Socket client) {
        this.client = client;
    }

    @Override
    public void run() {

        Log.e(TAG, "有客户端连接上");
        isConnecting = true;
        try {
            // 获取输入输出流
            out = new BufferedOutputStream(client.getOutputStream());
            in  = new BufferedInputStream(client.getInputStream());

            // 循环等待,接受PC端的命令
            while (isConnecting) {
                try {
                    if (!client.isConnected()) {
                        break;
                    }
                    // 读取命令
                    cmd = readCMDFromSocket(in);    
                    Log.e(TAG, "读取到PC发送的命令" + cmd);
                    /* 根据命令分别处理数据 */
                    if (cmd.equals(Constant.CONNECT)) {// 收到连接命令
                        EventBus.getDefault().post(new MessageEvent(Constant.START));
                        out.flush();
                    } else if (cmd.equalsIgnoreCase(Constant.DISCONNECT)) {// 断开命令
                        EventBus.getDefault().post(new MessageEvent(Constant.STOP));
                        out.flush();
                    }else if (cmd.equals(Const    ant.TAKEPHOTO)) {//拍照命令
                        EventBus.getDefault().post(new MessageEvent(Constant.TAKEPHOTO));
                        out.flush();
                    }
                    in.reset();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            out.close();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public void cancel() {
        isConnecting = false;
    }

    //发送数据
    public void writeData(byte[] data) {
        if (out != null) {
            try {
                out.write((data));
            } catch (IOException e) {
                Log.e(TAG, "输入输出异常");
                e.printStackTrace();
            }
        }
    }

    /* 读取命令 */
    public String readCMDFromSocket(InputStream in) {

        int MAX_BUFFER_BYTES = 2048;
        String msg = "";
        byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
        try {
            int numReadedBytes = in.read(tempbuffer, 0, tempbuffer.length);
            msg = new String(tempbuffer, 0, numReadedBytes, "utf-8");
            tempbuffer = null;
        } catch (Exception e) {
            Log.e(TAG, "readCMDFromSocket读数异常" + e.toString());
            EventBus.getDefault().post(new MessageEvent(Constant.DISCONNECT));
            e.printStackTrace();
        }
        return msg;
    }
}
复制代码

(5)主界面接收EventBus传递的命令信息 通信的通知过程采用EventBus实现,在主界面中注册EventBus。 onCreate中注册EventBus

EventBus.getDefault().register(this);
复制代码

创建onMessageEvent方法实现事件处理函数,并将事件处理交给主线程。

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        switch (event.message) {
        case Constant.DISCONNECT://断开命令

            Toast.makeText(MainActivity.this, "客户端断开", Toast.LENGTH_LONG)
                    .show();
            threadSocket.cancel();

        case Constant.START://收到连接命令

            isOpen = true;
            Toast.makeText(MainActivity.this, "客户端连接上", Toast.LENGTH_LONG)
                    .show();
            startCamera(curCameraIndex);

            break;
        case Constant.STOP://收到断开命令

            stopCamera();
            isOpen = false;

            break;
        case Constant.TAKEPHOTO://收到拍照命令

            if (isOpen) {
                camera.takePicture(new ShutterCallback() {

                    @Override
                    public void onShutter() {

                    }
                }, new Camera.PictureCallback() {

                    @Override
                    public void onPictureTaken(byte[] arg0, Camera arg1) {

                    }
                }, new Camera.PictureCallback() {

                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {

                        // 向电脑端发送数据
                        int datalen = data.length;
                        if (isOpen) {
                            // 写入数据头
                            sendData(new byte[] { (byte) 0xA1 });
                            // 写入数组长度
                            sendData(intToByteArray(datalen));
                            // 写入数据值
                            sendData(data);
                        }
                        // 重新开启预览
                        camera.stopPreview();
                        camera.startPreview();
                    }
                });

            }

        }
    }   
复制代码

6)当建立连接之后可以向PC端发送数据。 数据的发送需要调用ThreadReadWriterIOSocket的write方法,将图像信息数据发送给PC。

发送方法sendData如下:

public void sendData(final byte[] data) {
        threadSocket.writeData(data);
    }
复制代码

3.3 PC端发起连接

PC端作为客户端需要向服务端发起连接。PC的端的程序使用Java实现。主函数代码如下:

 public class Main {

        //用于通信的socket
        private static Socket socket;
        //输入输出数据流
        private static OutputStream out;
        private static InputStream in;
        public  static MyFrame frame;
        private static String CMD = "";

        public static void main(String[] args) {

            //创建界面
            frame = new MyFrame();
            //添加连接按钮
            JPanel panel2 = new JPanel();
            panel2.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0));
            final JButton button = new JButton("连接");
            panel2.add(button);
            button.addActionListener(new ActionListener() {
                @Override
            public void actionPerformed(ActionEvent arg0) {
                new Thread() {
                    public void run() {
                            //点击连接按钮,PC端发起连接
                            onConnect();
                        };
                    }.start();
                    button.setEnabled(false);
                }
            });
            //创建断开按钮
            JButton button2 = new JButton("断开");
            button2.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {

                    //发送断开命令
                    CMD = Constant.DISCONNECT;
                    try {
                        out.flush();
                        out.write(CMD.getBytes());
                        out.flush();                
                    } catch (IOException e) {

                        e.printStackTrace();
                    }
                    button.setEnabled(true);
                }
            });
            panel2.add(button2);

            //创建拍照按钮
            JButton button3 = new JButton("拍照");
            button3.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    //发送拍照命令
                    CMD = Constant.TAKEPHOTO;
                    try {
                        out.flush();
                        out.write(CMD.getBytes());
                        out.flush();                    
                    } catch (IOException e) {   
                        e.printStackTrace();
                    }           
                }
            });
            panel2.add(button3);

            frame.add(panel2, BorderLayout.SOUTH);
            frame.setResizable(false);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        }

        //启动连接时使用adb发送系统广播
        protected static void onConnect() {
            try {
                //adb指令,发送NotifyServiceStop广播,手机接收到此广播停止监听
                Runtime.getRuntime().exec(
                    "adb shell am broadcast -a NotifyServiceStop");
                Thread.sleep(2000);
                //调用adb的端口转换命令,将PC端发起的tcp连接指向手机服务器
                Runtime.getRuntime().exec("adb forward tcp:12580 tcp:10086"); // 端口转换
                Thread.sleep(2000);
                //发送NotifyServiceStart广播,手机接收到此广播启动监听
                Runtime.getRuntime().exec(
                    "adb shell am broadcast -a NotifyServiceStart");
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            int len = 0;
            int degree = 0;
            try {
                //PC端发起TCP连接
                InetAddress serveraddr = null;
                serveraddr = InetAddress.getByName("127.0.0.1");
                System.out.println("TCP 1 " + "正在连接。。。。");
                socket = new Socket(serveraddr, 12580);
                System.out.println("TCP 2 " + "连接成功 ");
                // 获取输入输出流
                out = new BufferedOutputStream(socket.getOutputStream());
                in  = new BufferedInputStream(socket.getInputStream());
                // 发送连接命令
                CMD = Constant.CONNECT;
                out.write(CMD.getBytes());
                out.flush();
                boolean flag = true;    
                if (socket.isConnected()) {
                    while (flag) {
                        try {
                            int head = in.read();

                            if (head == 0xA0) {//获取数据头
                                byte[] src = new byte[4];
                                len = in.read(src);
                                len = byteArrayToInt(src);//获取数据长度
                                if (len > 0) {
                                    int cclen = 0;
                                    byte[] srcData = new byte[len];
                                    while (cclen < len) {
                                        int readlen = in.read(srcData, cclen, len
                                            - cclen);
                                        cclen += readlen;
                                    }
                                    //将图像数据转为ImageIcon对象
                                    ImageIcon icon = new ImageIcon(srcData);
                                if (icon != null) {
                                    //显示图像
                                    frame.setIcon(icon, degree);
                                }
                            }
                            }else if (head == 0xA1) {//获取数据头
                                byte[] src = new byte[4];
                                len = in.read(src);
                                len = byteArrayToInt(src);//获取数据长度
                                if (len > 0) {
                                    int cclen = 0;
                                    byte[] srcData = new byte[len];
                                    while (cclen < len) {
                                        int readlen = in.read(srcData, cclen, len
                                            - cclen);
                                        cclen += readlen;
                                    }

                                    //保存图片文件
                                    File file = new File("D:\\images\\" + System.currentTimeMillis() + ".jpg");
                                    if (!file.exists()) {
                                        file.mkdirs();
                                    }
                                    //存储图片                      
                                    ImageIcon icon = new ImageIcon(srcData);
                                    Image image = icon.getImage();
                                    BufferedImage bi = new BufferedImage(image.getWidth(null),image.getHeight(null),BufferedImage.TYPE_INT_RGB);
                                    Graphics2D g2d = bi.createGraphics();
                                    g2d.drawImage(image, 0, 0, null);
                                    g2d.dispose();
                                    ImageIO.write(bi,"jpg",file);
                                }
                            }

                        } catch (Exception e) {

                        }

                    }
                }
            } catch (Exception e1) {
                System.out.println(" 连接出现异常:连接失败: " + e1.toString());
            }
        }

        public static int byteArrayToInt(byte[] b) {
            return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16
                | (b[0] & 0xFF) << 24;
        }
    }
复制代码

MyFrame类:

public class MyFrame extends JFrame {

        private static final long serialVersionUID = 1L;
        public MyPanel panel;

        public MyFrame() {

            // 默认的窗体名称
            this.setTitle("USB连接显示");

            // 获得面板的实例
            panel = new MyPanel();
            this.add(panel);

            this.pack();
            this.setVisible(true);
        }

        //设置图片
        public void setIcon(ImageIcon incon, int drgree) {  
            panel.setImage(incon, drgree);
        }
    }
复制代码

MyPanel类:

 public class MyPanel extends Panel {

        private static final long serialVersionUID = 1L;
        private Image bufferImage;
        private final Image screenImage = new BufferedImage(800, 600, 2);
        private final Graphics2D screenGraphic = (Graphics2D) screenImage
            .getGraphics();
        int degree = 90;
        private Image backgroundImage = screenImage;

        public MyPanel() {
            // 设定焦点在本窗体
            setFocusable(true);
            // 设定初始构造时面板大小,这里先采用图片的大小
            setPreferredSize(new Dimension(800, 600));
            // 绘制背景
            drawView();
        }

        public void setImage(ImageIcon icon, int degree) {

            //System.out.println("设置图片   偏转角度" + degree);
            this.degree = degree;
            backgroundImage = icon.getImage();
            drawView();
            repaint();
        }

        private void drawView() {
            int width  = getWidth();
            int height = getHeight();

            int x = width / 2 - backgroundImage.getWidth(null) /2;
            int y = height / 2 - backgroundImage.getHeight(null) / 2;
            screenGraphic.drawImage(backgroundImage, x , y, null);
        }

        @Override
        public void update(Graphics g) {

            if (bufferImage == null) {
                bufferImage = this.createImage(this.getWidth(), this.getHeight());
            }

            //bufferImage = rotateImage(bufferimage, degree);

            Graphics gBuffer = bufferImage.getGraphics();// 获得图片缓冲区的画笔
            if (gBuffer != null){
                paint(gBuffer);
            }
            else{
                paint(g);
            }
            gBuffer.dispose();
            g.drawImage(bufferImage, 0, 0, null);
        }

        //显示图像
        public void paint(Graphics g) {
            g.drawImage(screenImage, 0, 0, null);
        }


        public static BufferedImage rotateImage(final BufferedImage bufferedimage,
            final int degree) {
            int w = bufferedimage.getWidth();
            int h = bufferedimage.getHeight();
            int type = bufferedimage.getColorModel().getTransparency();
            BufferedImage img;
            Graphics2D graphics2d;
            (graphics2d = (img = new BufferedImage(w, h, type))
                .createGraphics()).setRenderingHint(
                RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            graphics2d.rotate(Math.toRadians(degree), w / 2, h / 2);
            graphics2d.drawImage(bufferedimage, 0, 0, null);
            graphics2d.dispose();
            return img;
        }
    }
复制代码

两个工程均用到的Constant类

public class Constant {
        public static final String addrIp = "192.168.0.119";
        public static final int addrPort = 56168;
        public static final String CONNECT = "CONNECT";
        public static final String DISCONNECT = "DISCONNECT";
        public static final String TAKEPHOTO = "TAKEPHOTO";
        public static final String START = "START";
        public static final String STOP = "STOP";
    }
复制代码

本片文章到这里就结束了,后续会不断更新Android干货,面试题分享希望对大家有所帮助。

公众号:程序员喵大人(专注于Android各类学习笔记、面试题以及IT类资讯的分享。)

分类:
Android