android development

android

配置

使用 android studio 的时候使用gradle同步的时候,如果配置了socket proxy,那么gradle同步时,会提示是否要设置gradle的配置,这时候要点取消,否则会报错表示socket连接有问题

android camera2 api

框架

android_camera.png

操作API

  • mContext.getSystemService(Context.CAMERA_SERVICE);

    用于获取系统的cameraManager

  • id = mCameraManager.getCameraIdList()

    mCameraManager.getCameraCharacteristics(id)

    选择不同的摄像头

  • mCameraManager.openCamera(mCameraId, mStateCallback, null);

    根据id打开摄像头,并设置callback,callback决定了摄像头不同状态时会触发什么函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
    // 打开相机时调用此方法。 在这里开始相机预览。
    mCameraDevice = cameraDevice;
    //创建CameraPreviewSession
    Log.i(TAG, "onOpened: before createCameraPreviewSession");
    createCameraPreviewSession();
    Log.i(TAG, "onOpened: after createCameraPreviewSession");

    }
    }
  • createCameraPreviewSession()

    根据captureRequestBuilder的设置,通过manager设置一个 caputrueSession,不断发送请求。

    createCaptureSession 设定了返回的值的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    mCaptureRequestBuilder.addTarget(mSurface);
    mCaptureRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL);
    mCameraDevice.createCaptureSession(Collections.singletonList(mSurface), new CameraCaptureSession.StateCallback() {
    @Override
    public void onConfigured(@NonNull CameraCaptureSession session) {
    //构建captureRequest对象
    //设置人脸检测
    // 会话准备好后,我们开始显示预览
    mCaptureSession = session;
    try {
    // 自动对焦
    mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
    // 最终开启相机预览并添加事件
    mPreviewRequest = mCaptureRequestBuilder.build();
    mCaptureSession.setRepeatingRequest(mPreviewRequest,
    mCaptureCallback, mBackgroundHandler);
    } catch (CameraAccessException e) {
    e.printStackTrace();
    }
    }
  • CameraCaptureSession.CaptureCallback mCaptureCallback

    根据callback设定返回的结果和如何处理(有一部分

    1
    2
    3
    4
    5
    6
    7
    8
    private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
    /**
    * 对摄像头返回的结果进行处理,并获取人脸数据
    * @param result 摄像头数据
    */
    private void process(CaptureResult result) {
    // do something
    }

网络通信

请求到http服务器,并通过post请求发送图片数据

  • 注意事项:不要在主进程发送http请求,会报错解决方法1. 通过handler创建新的进程 2. 用方法忽略掉

    1
    2
    3
    4
    5
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    @SuppressLint("NewApi")
    // function
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
  • 将bitmap 压缩成png图片,并且转码为base64字符串,用utf-8编码(注意,如果不用utf-8编码,在post的时候有些特殊字符会变成空格,比如+会变成空格)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);

    baos.flush();
    baos.close();

    byte[] bitmapBytes = baos.toByteArray();
    result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
    String key = URLEncoder.encode(encoded, "utf-8");
    return key
  • 将base64字符串发送到服务器,并接受返回文本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // input string, url_adress
    URL url = new URL(url_adress);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setConnectTimeout(50000);//超时时间
    conn.setRequestMethod("POST");
    conn.setDoOutput(true);
    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());

    out.write(string);
    out.flush();
    out.close();
    InputStream inputStream = conn.getInputStream();
    byte[] data = StreamTool.read(inputStream);
    html = new String(data, "utf-8");

    return html;


    class StreamTool {
    public static byte[] read(InputStream inStream) throws Exception {
    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int len = 0;
    while ((len = inStream.read(buffer)) != -1) {
    outStream.write(buffer, 0, len);
    }
    inStream.close();
    return outStream.toByteArray();
    }
    }

多线程编程

因为非主线程不能修改ui界面,所以操作非主线程需要让一个主进程的handler来修改主线程的内容

  • 实现一个集成Thread的类,override其中的run()

  • 如果需要将信息返回到主进程修改界面

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      private Handler uiHandler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
      super.handleMessage(msg);
      mTextView.setText("face");
      }
      };

      // ......
      Message msg = new Message();
      msg.what = 0;
      msg.arg1 = response;
      uiHandler.sendMessage(msg);

列表 listview

自定义item的格式

  • 首先需要一个xml文件描述item

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="15dp">

    <ImageView
    android:id="@+id/IamgeView_List"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:background="#FFFFFF" />

    <TextView
    android:id="@+id/TextView_List"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="15dp"
    android:layout_marginTop="30dp"
    android:text="内容"
    android:textSize="30sp" />

    </LinearLayout>

自定义listAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ImageListAdapter extends ArrayAdapter<ImageListArray> {
private int recourceId;

public ImageListAdapter(Context context, int resource, List<ImageListArray> objects) {
super(context, resource, objects);
recourceId = resource;

}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
ImageListArray imageListArray = getItem(position); //得到集合中指定位置的一组数据,并且实例化
View view = LayoutInflater.from(getContext()).inflate(recourceId, parent, false); //用布局裁剪器(又叫布局膨胀器),将导入的布局裁剪并且放入到当前布局中
ImageView imageView = (ImageView) view.findViewById(R.id.IamgeView_List);//从裁剪好的布局里获取ImageView布局ID
TextView textView = (TextView) view.findViewById(R.id.TextView_List); //从裁剪好的布局里获取TextView布局Id
imageView.setImageBitmap(imageListArray.getImage());//将当前一组imageListArray类中的图片iamgeId导入到ImageView布局中
textView.setText(imageListArray.getLabel());//将当前一组imageListArray类中的TextView内容导入到TextView布局
return view;
}
}

显示list的内容

1
2
ImageListAdapter imageListAdapter = new ImageListAdapter(MainActivity.this, R.layout.item_layout, imageList);
listView.setAdapter(imageListAdapter);

fragment

fragment是一种较为方便讲每个显示的界面模块化的方法

生命周期

fragment_lifecycle.png

生成fragment的同时调用其中的控件

如果想在生成fragment的时候获取fragment中的resource(比如listView),则必须在onCreateView()之后,通过view.findViewByID才可以找得到,代码如下

1
2
3
4
5
6
7
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_record_layout, container, false);
mListView = view.findViewById(R.id.img_listview);
return view;
}

fragment的静态引用

注意这一段必须在一个layout里面

1
2
3
4
5
6
<fragment
android:id="@+id/fragment_record"
android:name="com.zhu.driveractionrecognition.RecordFragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />

其中name表示了这个fragment对应的类的类名