第九章:CameraX實現防微信朋友圈拍照和錄屏(附oss上傳下載manager)

  • 效果

在這裏插入圖片描述

  • 引入

    //圖片加載
    api 'com.github.bumptech.glide:glide:4.11.0'
    api 'jp.wasabeef:glide-transformations:4.0.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'


    //camera相機使用
    api 'androidx.camera:camera-core:1.0.0-alpha04'
    api 'androidx.camera:camera-camera2:1.0.0-alpha04'
    api 'androidx.camera:camera-view:1.0.0-alpha01'
    api 'androidx.camera:camera-extensions:1.0.0-alpha01'
    //gesture imageview 圖片預覽
    api 'com.github.chrisbanes:PhotoView:2.3.0@aar'

    //視頻播放組件
    api 'com.google.android.exoplayer:exoplayer-core:2.10.4'
    api 'com.google.android.exoplayer:exoplayer-dash:2.10.4'
    api 'com.google.android.exoplayer:exoplayer-ui:2.10.4'
  • 權限
	<uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

主進入頁面
  • xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        tools:context="test.camerax.study.MainCaptureActivity">

        <com.google.android.exoplayer2.ui.PlayerView
            android:id="@+id/player_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone"
            app:buffered_color="#008577"
            app:resize_mode="fixed_width"
            app:show_buffering="when_playing"
            app:surface_type="texture_view"
            app:use_controller="true" />

        <com.github.chrisbanes.photoview.PhotoView
            android:id="@+id/photo_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_marginTop="90dp"
            android:layout_marginBottom="60dp"
            android:scaleType="fitCenter"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="前去體驗CameraX"
            android:textAllCaps="true"
            android:textColor="#ff0000"
            android:textSize="20dp"
            android:textStyle="bold" />
    </FrameLayout>
</layout>

  • activity

public class MainCaptureActivity extends AppCompatActivity {

    private ActivityMainCaptureBinding mPreviewBinding;
    public static final String RESULT_FILE_PATH = "file_path";
    public static final String RESULT_FILE_WIDTH = "file_width";
    public static final String RESULT_FILE_HEIGHT = "file_height";
    public static final String RESULT_FILE_TYPE = "file_type";
    private String previewUrl;
    private boolean isVideo;
    private int mWidth;
    private int mHeight;
    private static final String[] PERMISSIONS = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO};
    private ArrayList<String> deniedPermission = new ArrayList<>();
    private static final int PERMISSION_CODE = 1000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPreviewBinding = DataBindingUtil.setContentView(this, R.layout.activity_main_capture);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        mPreviewBinding.tvContent.setOnClickListener(v -> {
            ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_CODE);
        });

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_CODE) {
            deniedPermission.clear();
            for (int i = 0; i < permissions.length; i++) {
                String permission = permissions[i];
                int result = grantResults[i];
                if (result != PackageManager.PERMISSION_GRANTED) {
                    deniedPermission.add(permission);
                }
            }
            if (deniedPermission.isEmpty()) {
                CaptureActivity.startActivityForResult(this);
                if (player != null) {
                    player.setPlayWhenReady(false);
                    player.stop(true);
                    player.release();
                }
            } else {
                new AlertDialog.Builder(this)
                        .setMessage("必須的權限沒有得到授權,功能將無法使用 請重新授權!")
                        .setNegativeButton("不", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                MainCaptureActivity.this.finish();
                            }
                        })
                        .setPositiveButton("好的", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                String[] denied = new String[deniedPermission.size()];
                                ActivityCompat.requestPermissions(MainCaptureActivity.this, deniedPermission.toArray(denied), PERMISSION_CODE);
                            }
                        }).create().show();
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CaptureActivity.REQ_CAPTURE && resultCode == RESULT_OK) {
            previewUrl = data.getStringExtra(RESULT_FILE_PATH);
            isVideo = data.getBooleanExtra(RESULT_FILE_TYPE, false);
            mWidth = data.getIntExtra(RESULT_FILE_WIDTH, 1080);
            mHeight = data.getIntExtra(RESULT_FILE_HEIGHT, 1920);
            StringBuffer stringBuffer = new StringBuffer();
            if (isVideo) {
                stringBuffer.append("恭喜你錄製視頻成功;");
                previewVideo(previewUrl);
            } else {
                stringBuffer.append("恭喜你拍照成功;");
                previewImage(previewUrl);
            }
            stringBuffer.append("寬度:" + mWidth + ",高度:" + mHeight);
            mPreviewBinding.tvContent.setText(stringBuffer);
        }
    }

    private SimpleExoPlayer player;

    private void previewVideo(String previewUrl) {
        mPreviewBinding.playerView.setVisibility(View.VISIBLE);
        mPreviewBinding.photoView.setVisibility(View.GONE);
        player = ExoPlayerFactory.newSimpleInstance(this, new DefaultRenderersFactory(this), new DefaultTrackSelector(), new DefaultLoadControl());

        Uri uri = null;
        File file = new File(previewUrl);
        if (file.exists()) {
            DataSpec dataSpec = new DataSpec(Uri.fromFile(file));
            FileDataSource fileDataSource = new FileDataSource();
            try {
                fileDataSource.open(dataSpec);
                uri = fileDataSource.getUri();
            } catch (FileDataSource.FileDataSourceException e) {
                e.printStackTrace();
            }
        } else {
            uri = Uri.parse(previewUrl);
        }

        ProgressiveMediaSource.Factory factory = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(this, Util.getUserAgent(this, getPackageName())));
        ProgressiveMediaSource mediaSource = factory.createMediaSource(uri);
        player.prepare(mediaSource);
        player.setPlayWhenReady(true);
        mPreviewBinding.playerView.setPlayer(player);
    }

    private void previewImage(String previewUrl) {
        mPreviewBinding.photoView.setVisibility(View.VISIBLE);
        mPreviewBinding.playerView.setVisibility(View.GONE);
        Glide.with(this).load(previewUrl).into(mPreviewBinding.photoView);
    }


    @Override
    protected void onPause() {
        super.onPause();
        if (player != null) {
            player.setPlayWhenReady(false);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (player != null) {
            player.setPlayWhenReady(true);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (player != null) {
            player.setPlayWhenReady(false);
            player.stop(true);
            player.release();
        }
    }

}


拍照頁面

  • 自定義按鈕

/**
 * 拍照使用
 */
public class RecordView extends View implements View.OnLongClickListener, View.OnClickListener {

    private static final int PROGRESS_INTERVAL = 100;
    private final Paint fillPaint;
    private final Paint progressPaint;
    private int progressMaxValue;
    private final int radius;
    private final int progressWidth;
    private final int progressColor;
    private final int fillColor;
    private final int maxDuration;
    private int progressValue;
    private boolean isRecording;
    private long startRecordTime;
    private onRecordListener mListener;

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

    public RecordView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RecordView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public RecordView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordView, defStyleAttr, defStyleRes);
        radius = typedArray.getDimensionPixelOffset(R.styleable.RecordView_radius, 0);
        progressWidth = typedArray.getDimensionPixelOffset(R.styleable.RecordView_progress_width, PixUtils.dp2px(3));
        progressColor = typedArray.getColor(R.styleable.RecordView_progress_color, Color.RED);
        fillColor = typedArray.getColor(R.styleable.RecordView_fill_color, Color.WHITE);
        maxDuration = typedArray.getInteger(R.styleable.RecordView_duration, 10);
        setMaxDuration(maxDuration);
        typedArray.recycle();

        fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        fillPaint.setColor(fillColor);
        fillPaint.setStyle(Paint.Style.FILL);

        progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        progressPaint.setColor(progressColor);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(progressWidth);

        Handler handler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                progressValue++;
                postInvalidate();
                if (progressValue <= progressMaxValue) {
                    sendEmptyMessageDelayed(0, PROGRESS_INTERVAL);
                } else {
                    finishRecord();
                }
            }
        };
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    isRecording = true;
                    startRecordTime = System.currentTimeMillis();
                    handler.sendEmptyMessage(0);
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    long now = System.currentTimeMillis();
                    if (now - startRecordTime > ViewConfiguration.getLongPressTimeout()) {
                        finishRecord();
                    }
                    handler.removeCallbacksAndMessages(null);
                    isRecording = false;
                    startRecordTime = 0;
                    progressValue = 0;
                    postInvalidate();
                }
                return false;
            }
        });

        setOnClickListener(this);
        setOnLongClickListener(this);
    }


    private void finishRecord() {
        if (mListener != null) {
            mListener.onFinish();
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        if (isRecording) {

            canvas.drawCircle(width / 2, height / 2, width / 2, fillPaint);

            int left = progressWidth / 2;
            int top = progressWidth / 2;
            int right = width - progressWidth / 2;
            int bottom = height - progressWidth / 2;
            float sweepAngle = (progressValue * 1.0f / progressMaxValue) * 360;
            canvas.drawArc(left, top, right, bottom, -90, sweepAngle, false, progressPaint);
        } else {
            canvas.drawCircle(width / 2, height / 2, radius, fillPaint);
        }
    }

    public void setMaxDuration(int maxDuration) {
        this.progressMaxValue = maxDuration * 1000 / PROGRESS_INTERVAL;
    }

    public void setOnRecordListener(onRecordListener listener) {

        mListener = listener;
    }

    @Override
    public boolean onLongClick(View v) {
        if (mListener != null) {
            mListener.onLongClick();
        }
        return true;
    }

    @Override
    public void onClick(View v) {
        if (mListener != null) {
            mListener.onClick();
        }
    }

    public interface onRecordListener {
        void onClick();

        void onLongClick();

        void onFinish();
    }
}



<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="RecordView">
        <attr name="progress_color" format="color"></attr>
        <attr name="progress_width" format="dimension"></attr>
        <attr name="fill_color" format="color"></attr>
        <attr name="radius" format="dimension"></attr>
        <attr name="duration" format="integer"></attr>
    </declare-styleable>
</resources>
  • 拍照xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextureView
            android:id="@+id/texture_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/capture_tips"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|bottom"
            android:layout_marginBottom="200dp"
            android:text="輕觸拍照,長按攝像"
            android:textColor="#ffffff" />

        <cn.yumakeji.lib_common.view.RecordView
            android:id="@+id/record_view"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="bottom|center_horizontal"
            android:layout_marginBottom="60dp"
            app:duration="10"
            app:fill_color="#ffffff"
            app:progress_color="@color/colorAccent"
            app:progress_width="4dp"
            app:radius="40dp" />
    </FrameLayout>
</layout>
  • CaptureActivity

import cn.yumakeji.jetpackroomstudy.R;
import cn.yumakeji.jetpackroomstudy.databinding.ActivityCaptureBinding;
import cn.yumakeji.lib_common.view.RecordView;

public class CaptureActivity extends AppCompatActivity {
    public static final int REQ_CAPTURE = 10001;
    private ActivityCaptureBinding mBinding;
    private Preview preview;
    private ImageCapture imageCapture;
    private VideoCapture videoCapture;
    private TextureView textureView;
    private String outputFilePath;

    public static void startActivityForResult(Activity activity) {
        Intent intent = new Intent(activity, CaptureActivity.class);
        activity.startActivityForResult(intent, REQ_CAPTURE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_capture);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        bindCameraX();
        initListener();
    }


    /**
     * 前後攝像頭
     */
    private CameraX.LensFacing mLensFacing = CameraX.LensFacing.BACK;
    /**
     * 旋轉角度
     */
    private int rotation = Surface.ROTATION_0;
    /**
     * 分辨率
     */
    private Size resolution = new Size(1280, 720);
    /**
     * 寬高比
     */
    private Rational rational = new Rational(9, 16);

    /**
     * 初始化配置
     */
    @SuppressLint("RestrictedApi")
    private void bindCameraX() {

        //查詢一下當前要使用的設備攝像頭(比如後置攝像頭)是否存在
        boolean hasAvailableCameraId = false;
        try {
            hasAvailableCameraId = CameraX.hasCameraWithLensFacing(mLensFacing);
        } catch (CameraInfoUnavailableException e) {
            e.printStackTrace();
        }

        if (!hasAvailableCameraId) {
            Toast.makeText(this, "無可用的設備cameraId!,請檢查設備的相機是否被佔用", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        //查詢一下是否存在可用的cameraId.形式如:後置:"0",前置:"1"
        String cameraIdForLensFacing = null;
        try {
            cameraIdForLensFacing = CameraX.getCameraFactory().cameraIdForLensFacing(mLensFacing);
        } catch (CameraInfoUnavailableException e) {
            e.printStackTrace();
        }
        if (TextUtils.isEmpty(cameraIdForLensFacing)) {
            Toast.makeText(this, "無可用的設備cameraId!,請檢查設備的相機是否被佔用", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        /**
         * 預覽功能
         */
        preview = new Preview(new PreviewConfig.Builder()
                //前後攝像頭
                .setLensFacing(mLensFacing)
                //旋轉角度
                .setTargetRotation(rotation)
                //分辨率
                .setTargetResolution(resolution)
                //寬高比
                .setTargetAspectRatio(rational)
                .build());
        /**
         * 拍照
         */
        imageCapture = new ImageCapture(new ImageCaptureConfig.Builder()
                //寬高比
                .setTargetAspectRatio(rational)
                //分辨率
                .setTargetResolution(resolution)
                //前後攝像頭
                .setLensFacing(mLensFacing)
                //ImageCapture.CaptureMode.MIN_LATENCY:快速合成
                //ImageCapture.CaptureMode.MAX_QUALITY:原圖
                .setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                //旋轉角度
                .setTargetRotation(rotation).build());

        /**
         * 視頻
         */
        videoCapture = new VideoCapture(new VideoCaptureConfig.Builder()
                .setTargetRotation(rotation)
                .setLensFacing(mLensFacing)
                .setTargetResolution(resolution)
                .setTargetAspectRatio(rational)
                //視頻幀率
                .setVideoFrameRate(25)
                //bit率
                .setBitRate(3 * 1024 * 1024).build());

        //視頻流回調
        preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput output) {

                textureView = mBinding.textureView;
                ViewGroup parent = (ViewGroup) textureView.getParent();
                //先removeView後addView才被渲染上來
                parent.removeView(textureView);
                parent.addView(textureView, 0);

                textureView.setSurfaceTexture(output.getSurfaceTexture());
            }
        });

        //上面配置的都是我們期望的分辨率
        List<UseCase> newUseList = new ArrayList<>();
        newUseList.add(preview);
        newUseList.add(imageCapture);
        newUseList.add(videoCapture);

        //下面我們要查詢一下 當前設備它所支持的分辨率有哪些,然後再更新一下 所配置的幾個usecase
        Map<UseCase, Size> resolutions = CameraX.getSurfaceManager()
                .getSuggestedResolutions(cameraIdForLensFacing, null, newUseList);

        Iterator<Map.Entry<UseCase, Size>> iterator = resolutions.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<UseCase, Size> next = iterator.next();
            UseCase useCase = next.getKey();
            Size value = next.getValue();
            Map<String, Size> update = new HashMap<>();
            update.put(cameraIdForLensFacing, value);
            useCase.updateSuggestedResolution(update);
        }
        CameraX.bindToLifecycle(this, preview, imageCapture, videoCapture);
    }

    private boolean takingPicture;

    /**
     * 拍照操作
     */
    private void initListener() {
        mBinding.recordView.setOnRecordListener(new RecordView.onRecordListener() {
            @Override
            public void onClick() {
                takingPicture = true;
                File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                        System.currentTimeMillis() + ".jpeg");
                mBinding.captureTips.setVisibility(View.INVISIBLE);
                imageCapture.takePicture(file, new ImageCapture.OnImageSavedListener() {
                    @Override
                    public void onImageSaved(@NonNull File file) {
                        onFileSaved(file);
                    }

                    @SuppressLint("RestrictedApi")
                    @Override
                    public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) {
                        ArchTaskExecutor.getMainThreadExecutor().execute(() -> {
                            Toast.makeText(CaptureActivity.this, message, Toast.LENGTH_SHORT).show();
                        });
                    }
                });
            }

            @SuppressLint("RestrictedApi")
            @Override
            public void onLongClick() {
                takingPicture = false;
                File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                        System.currentTimeMillis() + ".mp4");
                videoCapture.startRecording(file, new VideoCapture.OnVideoSavedListener() {
                    @Override
                    public void onVideoSaved(File file) {
                        onFileSaved(file);
                    }

                    @Override
                    public void onError(VideoCapture.UseCaseError useCaseError, String message, @Nullable Throwable cause) {
                        ArchTaskExecutor.getMainThreadExecutor().execute(() -> {
                            Toast.makeText(CaptureActivity.this, message, Toast.LENGTH_SHORT).show();
                        });
                    }
                });
            }

            @SuppressLint("RestrictedApi")
            @Override
            public void onFinish() {
                videoCapture.stopRecording();
            }
        });
    }

    /**
     * 獲得視頻、圖片去預覽
     *
     * @param file
     */
    private void onFileSaved(File file) {
        outputFilePath = file.getAbsolutePath();
        String mimeType = takingPicture ? "image/jpeg" : "video/mp4";
        MediaScannerConnection.scanFile(this, new String[]{outputFilePath}, new String[]{mimeType}, null);
        PreviewActivity.startActivityForResult(this, outputFilePath, !takingPicture, "完成");
    }


    public static final String RESULT_FILE_PATH = "file_path";
    public static final String RESULT_FILE_WIDTH = "file_width";
    public static final String RESULT_FILE_HEIGHT = "file_height";
    public static final String RESULT_FILE_TYPE = "file_type";

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PreviewActivity.REQ_PREVIEW && resultCode == RESULT_OK) {
            Intent intent = new Intent();
            intent.putExtra(RESULT_FILE_PATH, outputFilePath);
            //當設備處於豎屏情況時,寬高的值 需要互換,橫屏不需要
            intent.putExtra(RESULT_FILE_WIDTH, resolution.getHeight());
            intent.putExtra(RESULT_FILE_HEIGHT, resolution.getWidth());
            intent.putExtra(RESULT_FILE_TYPE, !takingPicture);
            setResult(RESULT_OK, intent);
            finish();
        }
    }

    @Override
    public void onDetachedFromWindow() {
        CameraX.unbindAll();
        super.onDetachedFromWindow();
    }


}


預覽頁面

  • xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        android:orientation="vertical">

        <com.google.android.exoplayer2.ui.PlayerView
            android:id="@+id/player_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone"
            app:buffered_color="#008577"
            app:resize_mode="fixed_width"
            app:show_buffering="when_playing"
            app:surface_type="texture_view"
            app:use_controller="false" />


        <com.github.chrisbanes.photoview.PhotoView
            android:id="@+id/photo_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_marginTop="90dp"
            android:layout_marginBottom="60dp"
            android:scaleType="fitCenter"
            android:visibility="gone" />

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/action_close"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            app:srcCompat="@mipmap/icon_close"
            app:tint="#ffffff" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/action_ok"
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:layout_gravity="right|top"
            android:layout_marginTop="16dp"
            android:layout_marginRight="16dp"
            android:gravity="center"
            android:text="完成"
            app:backgroundTint="#008577"
            app:cornerRadius="5dp"/>
    </FrameLayout>
</layout>
  • PreviewActivity

public class PreviewActivity extends AppCompatActivity implements View.OnClickListener {
    public static final int REQ_PREVIEW = 1000;
    public static final String KEY_PREVIEW_URL = "preview_url";
    public static final String KEY_PREVIEW_VIDEO = "preview_video";
    public static final String KEY_PREVIEW_BTNTEXT = "preview_btntext";
    private ActivityPreviewBinding mPreviewBinding;
    private SimpleExoPlayer player;

    public static void startActivityForResult(Activity activity, String previewUrl, boolean isVideo, String btnText) {
        Intent intent = new Intent(activity, PreviewActivity.class);
        intent.putExtra(KEY_PREVIEW_URL, previewUrl);
        intent.putExtra(KEY_PREVIEW_VIDEO, isVideo);
        intent.putExtra(KEY_PREVIEW_BTNTEXT, btnText);
        activity.startActivityForResult(intent, REQ_PREVIEW);
        activity.overridePendingTransition(0, 0);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPreviewBinding = DataBindingUtil.setContentView(this, R.layout.activity_preview);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        String previewUrl = getIntent().getStringExtra(KEY_PREVIEW_URL);
        boolean isVideo = getIntent().getBooleanExtra(KEY_PREVIEW_VIDEO, false);
        String btnText = getIntent().getStringExtra(KEY_PREVIEW_BTNTEXT);
        if (TextUtils.isEmpty(btnText)) {
            mPreviewBinding.actionOk.setVisibility(View.GONE);
        } else {
            mPreviewBinding.actionOk.setVisibility(View.VISIBLE);
            mPreviewBinding.actionOk.setText(btnText);
            mPreviewBinding.actionOk.setOnClickListener(this);
        }

        mPreviewBinding.actionClose.setOnClickListener(this);

        if (isVideo) {
            previewVideo(previewUrl);
        } else {
            previewImage(previewUrl);
        }
    }

    /**
     * 預覽圖片
     *
     * @param previewUrl
     */
    private void previewImage(String previewUrl) {
        mPreviewBinding.photoView.setVisibility(View.VISIBLE);
        Glide.with(this).load(previewUrl).into(mPreviewBinding.photoView);
    }

    /**
     * 預覽視頻
     *
     * @param previewUrl
     */
    private void previewVideo(String previewUrl) {
        mPreviewBinding.playerView.setVisibility(View.VISIBLE);
        player = ExoPlayerFactory.newSimpleInstance(this, new DefaultRenderersFactory(this), new DefaultTrackSelector(), new DefaultLoadControl());

        Uri uri = null;
        File file = new File(previewUrl);
        if (file.exists()) {
            DataSpec dataSpec = new DataSpec(Uri.fromFile(file));
            FileDataSource fileDataSource = new FileDataSource();
            try {
                fileDataSource.open(dataSpec);
                uri = fileDataSource.getUri();
            } catch (FileDataSource.FileDataSourceException e) {
                e.printStackTrace();
            }
        } else {
            uri = Uri.parse(previewUrl);
        }

        ProgressiveMediaSource.Factory factory = new ProgressiveMediaSource.Factory(new DefaultDataSourceFactory(this, Util.getUserAgent(this, getPackageName())));
        ProgressiveMediaSource mediaSource = factory.createMediaSource(uri);
        player.prepare(mediaSource);
        player.setPlayWhenReady(true);
        mPreviewBinding.playerView.setPlayer(player);
    }


    @Override
    protected void onPause() {
        super.onPause();
        if (player != null) {
            player.setPlayWhenReady(false);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (player != null) {
            player.setPlayWhenReady(true);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (player != null) {
            player.setPlayWhenReady(false);
            player.stop(true);
            player.release();
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.action_close) {
            finish();
        } else if (v.getId() == R.id.action_ok) {
            setResult(RESULT_OK, new Intent());
            finish();
        }
    }

}

demo地址:https://gitee.com/huangxiaoguo/jetpackandrxjava


oss上傳下載文件

	//aliyun oss
    api 'com.aliyun.dpa:oss-android-sdk:+'

/**
 * 阿里雲oss 文件上傳
 */
public class FileUploadManager {
    private static OSSClient oss = null;
    private static final String ALIYUN_BUCKET_URL = "https://******.oss-cn-hangzhou.aliyuncs.com/";
    private static final String BUCKET_NAME = "******";
    private static final String END_POINT = "http://oss-cn-******.aliyuncs.com";
    private static final String AUTH_SERVER_URL = "http://******:7080/";

    static {

        OSSCredentialProvider credentialProvider = new OSSAuthCredentialsProvider(AUTH_SERVER_URL);
        //該配置類如果不設置,會有默認配置,具體可看該類
        ClientConfiguration conf = new ClientConfiguration();
        conf.setConnectionTimeout(15 * 1000); // 連接超時,默認15秒
        conf.setSocketTimeout(15 * 1000); // socket超時,默認15秒
        conf.setMaxConcurrentRequest(5); // 最大併發請求數,默認5個
        conf.setMaxErrorRetry(2); // 失敗後最大重試次數,默認2次
        OSSLog.disableLog(); //這個開啓會支持寫入手機sd卡中的一份日誌文件位置在SDCard_path\OSSLog\logs.csv

        oss = new OSSClient(AppGlobals.getApplication(), END_POINT, credentialProvider, conf);
    }

    //同步
    public static String upload(byte[] bytes) throws ClientException, ServiceException {
        String objectKey = String.valueOf(System.currentTimeMillis());
        PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectKey, bytes);
        PutObjectResult result = oss.putObject(request);
        if (result.getStatusCode() == 200) {
            return ALIYUN_BUCKET_URL + objectKey;
        } else {
            return null;
        }
    }

    //異步
    public static void upload(byte[] bytes, UploadCallback callback) {
        String objectKey = String.valueOf(System.currentTimeMillis());
        PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectKey, bytes);
        upload(request, callback);
    }

    //同步
    public static String upload(String filePath) {
        String objectKey = filePath.substring(filePath.lastIndexOf("/") + 1);
        PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectKey, filePath);
        PutObjectResult result = null;
        try {
            result = oss.putObject(request);
        } catch (ClientException e) {
            e.printStackTrace();
        } catch (ServiceException e) {
            e.printStackTrace();
        }
        if (result != null && result.getStatusCode() == 200) {
            return ALIYUN_BUCKET_URL + objectKey;
        } else {
            return null;
        }
    }

    //異步
    public static void upload(String filePath, UploadCallback callback) {
        String objectKey = filePath.substring(filePath.lastIndexOf("/") + 1);
        PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectKey, filePath);
        upload(request, callback);
    }

    private static void upload(final PutObjectRequest put, final UploadCallback callback) {
        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                Log.e("PutObject", "currentSize: " + currentSize + " totalSize: " + totalSize);
            }
        });
        OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
            @Override
            public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                String eTag = result.getETag();
                String serverCallbackReturnBody = result.getServerCallbackReturnBody();
                Log.e("PutObject", "UploadSuccess" + eTag + "--" + serverCallbackReturnBody);
                if (callback != null && result.getStatusCode() == 200) {
                    callback.onUpload(ALIYUN_BUCKET_URL + put.getObjectKey());
                }
            }

            @Override
            public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                printError(clientExcepion, serviceException);
                if (callback != null) {
                    callback.onError(serviceException.getRawMessage());
                }
            }
        });
    }

    public void download(String url, final String filePath, final DownloadCallback callback) {
        // 構造下載文件請求
        GetObjectRequest get = new GetObjectRequest(BUCKET_NAME, url);
        OSSAsyncTask task = oss.asyncGetObject(get, new OSSCompletedCallback<GetObjectRequest, GetObjectResult>() {
            @Override
            public void onSuccess(GetObjectRequest request, GetObjectResult result) {
                Log.d("Content-Length", "" + result.getContentLength());
                //FileUtil.SaveFile(filePath, result.getObjectContent());
                if (callback != null) {
                    callback.onDownloadSuccess(filePath);
                }
            }

            @Override
            public void onFailure(GetObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                // 請求異常
                printError(clientExcepion, serviceException);
                if (callback != null) {
                    callback.onError(serviceException.getRawMessage());
                }
            }
        });

    }

    private static void printError(ClientException clientExcepion, ServiceException serviceException) {
        // 請求異常
        if (clientExcepion != null) {
            // 本地異常如網絡異常等
            clientExcepion.printStackTrace();
        }
        if (serviceException != null) {
            // 服務異常
            Log.e("ErrorCode", serviceException.getErrorCode());
            Log.e("RequestId", serviceException.getRequestId());
            Log.e("HostId", serviceException.getHostId());
            Log.e("RawMessage", serviceException.getRawMessage());
        }
    }

    public interface UploadCallback {
        void onUpload(String result);

        void onError(String error);
    }

    public interface DownloadCallback {
        void onDownloadSuccess(String fileUrl);

        void onError(String error);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章