相機的使用 --- 拍照,相冊導入

一、相機的基本使用

1,獲取SurfaceView控件的初始化

        surfaceView = (SurfaceView) findViewById(R.id.surface_sv);
        holder = surfaceView.getHolder();
        surfaceView.setBackgroundColor(0);

        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        holder.addCallback(this);
2,添加CallBack事件監聽

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(camera, holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        camera.stopPreview();
        startPreview(camera, holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }
3,預覽相機並設置參數

    /**
     * 預覽相機
     */
    private void startPreview(Camera camera, SurfaceHolder holder) {
        try {
            setupCamera(camera);
            camera.setPreviewDisplay(holder);
            //親測的一個方法 基本覆蓋所有手機 將預覽矯正
            CameraUtil.getInstance().setCameraDisplayOrientation(this, 0, camera);
            camera.startPreview();
            if (camera.getParameters() != null) {
                Camera.Size size = camera.getParameters().getPreviewSize(); // 獲取預覽大小
                if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
                    initSurfaceView(size.height, size.width);
                } else {
                    initSurfaceView(size.width, size.height);
                }
            } else {
                Toast.makeText(MainActivity.this, "無法啓動相機", Toast.LENGTH_SHORT).show();
                finish();
            }
        } catch (Exception e) {
            Toast.makeText(MainActivity.this, "無法啓動相機", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
            finish();
        }
    }

    /**
     * 設置
     */
    private void setupCamera(Camera camera) {
        try {
            Camera.Parameters parameters = camera.getParameters();

            if (parameters.getSupportedFocusModes().contains(
                    Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            }

            //這裏第三個參數爲最小尺寸 getPropPreviewSize方法會對從最小尺寸開始升序排列 取出所有支持尺寸的最小尺寸
            Camera.Size previewSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPreviewSizes(), 800);
            parameters.setPreviewSize(previewSize.width, previewSize.height);

            Camera.Size pictrueSize = CameraUtil.getInstance().getPropSizeForHeight(parameters.getSupportedPictureSizes(), 800);
            parameters.setPictureSize(pictrueSize.width, pictrueSize.height);

            camera.setParameters(parameters);

            /**
             * 設置surfaceView的尺寸 因爲camera默認是橫屏,所以取得支持尺寸也都是橫屏的尺寸
             * 我們在startPreview方法裏面把它矯正了過來,但是這裏我們設置設置surfaceView的尺寸的時候要注意 previewSize.height<previewSize.width
             * previewSize.width纔是surfaceView的高度
             * 一般相機都是屏幕的寬度 這裏設置爲屏幕寬度 高度自適應 你也可以設置自己想要的大小
             *
             */
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(screenWidth, screenHeight);
            surfaceView.setLayoutParams(params);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this, "無法啓動相機", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
4,糾正和矯正Surfaceview

    /**
     * 初始化SurfaceView
     *
     * @param camera_w
     * @param camera_h
     */

    private void initSurfaceView(int camera_w, int camera_h) {

        int height = MainApplication.getInstance().getHeight()
                - ScreenUtil.getStatusBarHeight();
        int width = MainApplication.getInstance().getWidth();


        int w = (int) ((height * camera_w) / camera_h);
        int h = (int) ((camera_h * width) / camera_w);

        int h_outer = 0;
        int w_outer = 0;

        if (w <= width) {
            Log.e("", "高度爲屏幕高度");
            h_outer = height;
            w_outer = w;
        } else if (h <= height) {
            Log.e("", "寬度爲屏幕寬度");// ..
            h_outer = h;
            w_outer = width;
        } else {
            Log.e("", "都不合適========================");
        }

        if (w_outer > 0) {
            if (w_outer < width || h_outer < height) {
                //無法鋪滿全屏
                float scale_w = (float) width / (float) w_outer;
                float scale_h = (float) height / (float) h_outer;
                float scale = ((scale_h - scale_w) > 0) ? scale_h : scale_w;
                w_outer = (int) (w_outer * scale);
                h_outer = (int) (h_outer * scale);
            }
            preViewSize[0] = w_outer;
            preViewSize[1] = h_outer;
            FrameLayout.LayoutParams lpsurface = new FrameLayout.LayoutParams(w_outer, h_outer);
//            lpsurface.setMargins((width-w_outer)/2,(height-h_outer)/2,0,0);
            surfaceView.setLayoutParams(lpsurface);
            Log.e("111", "相機預覽:" + camera_w + "x" + camera_h);//960x1280
            Log.e("111", "surfaceview :" + w_outer + "x" + h_outer);//1305x1740   1080x1440
        }

    }
5,合適的地方釋放相機資源

    /**
     * 釋放相機資源
     */
    private void releaseCamera() {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera = null;
        }
    }

此時,相機應該可以正常使用。

【在4中,適配不同的相機預覽,便於獲取圖片不變形】

6,生命週期中的維護

    @Override
    protected void onResume() {
        super.onResume();
        if (camera == null) {
            camera = getCamera();
            if (holder != null) {
                startPreview(camera, holder);
            }
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseCamera();
    }

    /**
     * 獲取Camera實例
     *
     * @return
     */
    private Camera getCamera() {
        Camera camera = null;
        try {
            camera = Camera.open(0);
        } catch (Exception e) {

        }
        return camera;
    }

7,拍照

private void captrue() {
        camera.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                //將data 轉換爲位圖 或者你也可以直接保存爲文件使用 FileOutputStream
                //這裏我相信大部分都有其他用處把 比如加個水印 後續再講解
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                Bitmap saveBitmap = CameraUtil.getInstance().setTakePicktrueOrientation(0, bitmap);
                bitmap.recycle();

                bitmap = Bitmap.createScaledBitmap(saveBitmap, preViewSize[0], preViewSize[1], true);
                saveBitmap.recycle();
                rotateMIV.setmSrc(bitmap);

                changeToShowPic();
            }
        });
    }

二、圖片處理   ---  旋轉,縮放,截圖
1,圖片處理的主要實現類,由以下自定義控件實現
@SuppressLint("AppCompatCustomView")
public class MatrixImageView extends ImageView {
    private static final int MODE_NONE = 0x00123;// 默認的觸摸模式
    private static final int MODE_DRAG = 0x00321;// 拖拽模式
    private static final int MODE_ZOOM = 0x00132;// 縮放or旋轉模式

    private int mode;// 當前的觸摸模式

    private float preMove = 1F;// 上一次手指移動的距離
    private float saveRotate = 0F;// 保存了的角度值
    private float rotate = 0F;// 旋轉的角度

    private float[] preEventCoor;// 上一次各觸摸點的座標集合

    private PointF startPointF, midPointF;// 起點、中點對象


    private Matrix currentMatrix, savedMatrix;// 當前和保存了的Matrix對象


    /**
     * 新增截圖保存功能
     * 旋轉展示的圖片和頁面上截圖的矩形運算,形成新的圖片,將圖片保存成爲新圖片即可!
     * 1,觸發事件
     * 2,截圖矩形獲取
     * 3,圖片保存 -- 保存路徑
     */

    /**
     * 保存文件的路徑
     */
    private String fileURL;


    /**
     * 單字Code碼 -- 文件保存名稱
     */
    private String fontCode;


    /**
     * 截圖矩形
     */
    private Rect cutRect;

    //原始圖片
    private Bitmap mSrc;

    //控件的寬度
    private int mWidth;

    // 控件的高度
    private int mHeight;

    private PaintFlagsDrawFilter mDrawFilter;

    int i = 0;
    private Context context;

    public MatrixImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        // 初始化
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        // 實例化對象
        currentMatrix = new Matrix();
        savedMatrix = new Matrix();
        startPointF = new PointF();
        midPointF = new PointF();
        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        // 模式初始化
        mode = MODE_NONE;

        Drawable drawable = getDrawable();
        try {
            mSrc = drawableToBitamp(drawable);
        } catch (Exception e) {
            mSrc = null;
        }

        if (mSrc == null) {
            //如果爲空,賦默認值
            mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null);
        }

    }

    /**
     * 計算控件的高度和寬度
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 設置寬度
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        //match_parent或者設置的精確值獲取
        //MeasureSpec.EXACTLY
        if (specMode == MeasureSpec.EXACTLY) {
            mWidth = specSize;
        } else {
            // 由圖片決定的寬
            //getPaddingLeft(),getPaddingRight()這兩個值是控件屬性的向內偏移的距離值,所以的一起計算
            //區別於layout_marginLeft,兩個控件的左間距值設置
            int desireByImg = getPaddingLeft() + getPaddingRight()
                    + mSrc.getWidth();

            // wrap_content
            if (specMode == MeasureSpec.AT_MOST) {
                //所以最小的值,寬度的話是左右內偏移距離之和
                mWidth = Math.min(desireByImg, specSize);
            } else

                mWidth = desireByImg;
        }

        // 設置高度,部分解釋同上
        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);

        //match_parent或者設置的精確值獲取
        //MeasureSpec.EXACTLY
        if (specMode == MeasureSpec.EXACTLY) {
            mHeight = specSize;
        } else {
            int desire = getPaddingTop() + getPaddingBottom()
                    + mSrc.getHeight();

            // wrap_content
            if (specMode == MeasureSpec.AT_MOST) {
                mHeight = Math.min(desire, specSize);
            } else
                mHeight = desire;
        }

        //計算好的寬度以及高度是值,設置進去
        setMeasuredDimension(mWidth, mHeight);
    }

    //drawable轉bitmap
    private Bitmap drawableToBitamp(Drawable drawable) {
        //從控件的src獲取背景,也是drawable文件獲取
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        }

        //如果沒有繪圖一個,只不過是空白的圖片
//        int w = drawable.getIntrinsicWidth();
//        int h = drawable.getIntrinsicHeight();
        int w = MainApplication.getInstance().getWidth();
        int h = MainApplication.getInstance().getHeight();

        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    final Paint paint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        //消除鋸齒, 圖片旋轉後的鋸齒消除不成功,實在不行圖片邊緣加一些白色像素點
        canvas.setDrawFilter(mDrawFilter);
        //畫經過Matrix變化後的圖
        canvas.drawBitmap(mSrc, currentMatrix, null);

        /**
         * 測試切圖區域和顯示區域一致
         */
        if (cutRect != null) {
            paint.setColor(Color.RED);
            canvas.drawRect(cutRect, paint);
        }
    }


    /**
     * 裁剪圖片並保存到本地
     */
    public boolean cutBitmapAndSave() {
        try {
            Bitmap out = Bitmap.createBitmap(cutRect.width(), cutRect.height(), Bitmap.Config.ARGB_8888);
            //畫布在右上角
            Canvas outC = new Canvas(out);
            //平移畫布到和截圖區域一致【注意反向】
            outC.translate(-cutRect.left, -cutRect.top);
            outC.drawBitmap(mSrc, currentMatrix, null);

            String fileParent = Environment.getExternalStorageDirectory().toString() + "/camera/demo/";
            File fileDirectory = new File(fileParent);
            if (!fileDirectory.exists()) {
                boolean mkdirs = fileDirectory.mkdirs();
                Log.i("parent", mkdirs + "");
                if (mkdirs) {
                    File file = new File(fileParent + System.currentTimeMillis() + ".png");
                    if (!file.exists()) {
                        boolean newFile = file.createNewFile();
                        if (newFile) {
                            BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);
                            return true;
                        } else {
                            Toast.makeText(context, "創建文件失敗", Toast.LENGTH_SHORT).show();
                            return false;
                        }
                    } else {
                        BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);
                        return true;
                    }
                } else {
                    Toast.makeText(context, "創建文件夾失敗", Toast.LENGTH_SHORT).show();
                    return false;
                }
            } else {
                File file = new File(fileParent + System.currentTimeMillis() + ".png");
                if (!file.exists()) {
                    boolean newFile = file.createNewFile();
                    if (newFile) {
                        BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);
                        return true;
                    } else {
                        Toast.makeText(context, "創建文件失敗", Toast.LENGTH_SHORT).show();
                        return false;
                    }
                } else {
                    BitmapUtil.savePNG_After(MainApplication.getInstance(), out, file.toString(), 20);
                    return true;
                }
            }
//            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:// 單點接觸屏幕時
                savedMatrix.set(currentMatrix);
                startPointF.set(event.getX(), event.getY());
                //單點觸摸是移動模式
                mode = MODE_DRAG;
                preEventCoor = null;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:// 第二個點接觸屏幕時
                preMove = calSpacing(event);
                if (preMove > 10F) {
                    savedMatrix.set(currentMatrix);
                    // 計算兩個觸摸點的中點座標
                    calMidPoint(midPointF, event);
                    //兩點是旋轉或者縮放模式
                    mode = MODE_ZOOM;
                }
                preEventCoor = new float[4];
                preEventCoor[0] = event.getX(0);
                preEventCoor[1] = event.getX(1);
                preEventCoor[2] = event.getY(0);
                preEventCoor[3] = event.getY(1);
                saveRotate = calRotation(event);
                break;
            case MotionEvent.ACTION_UP:// 單點離開屏幕時
            case MotionEvent.ACTION_POINTER_UP:// 第二個點離開屏幕時
                mode = MODE_NONE;
                preEventCoor = null;
                break;
            case MotionEvent.ACTION_MOVE:// 觸摸點移動時
            /*
             * 單點觸控拖拽平移
             */
                if (mode == MODE_DRAG) {
                    currentMatrix.set(savedMatrix);
                    float dx = event.getX() - startPointF.x;
                    float dy = event.getY() - startPointF.y;
                    currentMatrix.postTranslate(dx, dy);
                }
            /*
             * 兩點觸控拖放旋轉
             */
                else if (mode == MODE_ZOOM && event.getPointerCount() == 2) {
                    float currentMove = calSpacing(event);
                    currentMatrix.set(savedMatrix);
                /*
                 * 指尖移動距離大於10F縮放
                 */
                    if (currentMove > 10F) {
                        float scale = currentMove / preMove;
                        currentMatrix.postScale(scale, scale, midPointF.x, midPointF.y);
                    }
                /*
                 * 保持兩點時旋轉
                 */
                    if (preEventCoor != null) {
                        rotate = calRotation(event);
                        r = rotate - saveRotate;
                        currentMatrix.postRotate(r, getMeasuredWidth() / 2, getMeasuredHeight() / 2);
                    }
                }
                break;
        }

        setImageMatrix(currentMatrix);
        return true;
    }

    float r;

    /**
     * 計算兩個觸摸點間的距離
     */
    private float calSpacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * 計算兩個觸摸點的中點座標
     */
    private void calMidPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    /**
     * 計算旋轉角度
     *
     * @param
     * @return 角度值
     */
    private float calRotation(MotionEvent event) {
        double deltaX = (event.getX(0) - event.getX(1));
        double deltaY = (event.getY(0) - event.getY(1));
        double radius = Math.atan2(deltaY, deltaX);
        return (float) Math.toDegrees(radius);
    }


    /**
     * 設置原始圖片
     *
     * @param mSrc
     */
    public void setmSrc(Bitmap mSrc) {
        this.mSrc = mSrc;
        currentMatrix = new Matrix();
        requestLayout();
        postInvalidate();
    }

    public void setFileURL(String fileURL) {
        this.fileURL = fileURL;
    }

    public void setFontCode(String fontCode) {
        this.fontCode = fontCode;
    }

    public void setCutRect(Rect cutRect) {
        this.cutRect = cutRect;
    }

    /**
     * 更新當前Matrix
     */
    public void setCurrentMatrix() {
        this.currentMatrix = new Matrix();
    }

} 

2,控件的正常使用,需要設置原始圖片的默認值

        if (mSrc == null) {
            //如果爲空,賦默認值
            mSrc = BitmapFactory.decodeResource(getResources(), R.mipmap.desc6, null);
        }
    /**
     * 設置原始圖片
     *
     * @param mSrc
     */
    public void setmSrc(Bitmap mSrc) {
        this.mSrc = mSrc;
        currentMatrix = new Matrix();
        requestLayout();
        postInvalidate();
    }

在切換到顯示圖片時,對控件設置原始圖片,並刷新顯示,繪製。


3,在Ondraw()中繪製裁剪區域
      /**
         * 測試切圖區域和顯示區域一致
         */
        if (cutRect != null) {
            paint.setColor(Color.RED);
            canvas.drawRect(cutRect, paint);
        }

在Activity中獲取裁剪區域大小:
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        cutRectIV.post(new Runnable() {
            @Override
            public void run() {
                Rect viewRect = new Rect();
                int[] location = new int[2];
                int[] location1 = new int[2];
//                cutRectIV.getLocalVisibleRect(viewRect);
                cutRectIV.getLocationOnScreen(location);
                contentTakePicRL.getLocationOnScreen(location1);
                /**
                 * getLeft() /getTop() /getBottom() /getRight()
                 * 都是相對父控件的位置
                 */
                //設置截圖區域
                viewRect.left = cutRectIV.getLeft();
                viewRect.top = contentTakePicRL.getTop();
                viewRect.bottom = cutRectIV.getBottom() + contentTakePicRL.getTop();
                viewRect.right = cutRectIV.getRight();

                rotateMIV.setCutRect(viewRect);
            }
        });
    }

特別注意:gettop(),getLeft()都是相對父佈局,在計算時,父佈局與實際所在的位置一定要清晰。



三、相冊導入

1,相冊導入一般使用系統相冊導入
注意點:
(1)自己構建相冊需要管理相冊內存;
(2)實際倒入效果並不會有很好的提升。
導入時注意對圖片進行壓縮:

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //獲取圖片路徑
        if (requestCode == IMAGE && resultCode == Activity.RESULT_OK && data != null) {
            Uri selectedImage = data.getData();
            String[] filePathColumns = {MediaStore.Images.Media.DATA};
            Cursor c = getContentResolver().query(selectedImage, filePathColumns, null, null, null);
            c.moveToFirst();
            int columnIndex = c.getColumnIndex(filePathColumns[0]);
            String imagePath = c.getString(columnIndex);

            int[] size = getImageWidthHeight(imagePath);
            int width = screenWidth;
            //導入進來,讓寬適配屏幕寬度
            int height = (int) (((float) (screenWidth * size[1])) / ((float) size[0]));


            Bitmap bitmap = BitmapUtil.getBitmapBySize(imagePath, width, height);

            rotateMIV.setmSrc(bitmap);

            changeToShowPic();
            c.close();
        }
    }

現獲取圖片大小,再依據最後顯示大小,壓縮圖片後再顯示。

    public static Bitmap getBitmapBySize(String path, int width, int height) {
        BitmapFactory.Options option = new BitmapFactory.Options();
        option.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, option);
        option.inSampleSize = computeSampleSize(option, -1, width * height);

        option.inJustDecodeBounds = false;
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeFile(path, option);
            if (bitmap.getWidth() != width || bitmap.getHeight() != height) {
                Matrix mx = new Matrix();
                mx.setScale((float) width / (float) bitmap.getWidth(), (float) height / (float) bitmap.getHeight());
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mx, true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return bitmap;
    }

2,保存圖片

在圖片保存中,存在已經設置權限了,還是創建目錄不成功的情形,需要動態設置權限。
onCreate()中添加權限動態訪問:

    ActivityCompat.requestPermissions(MainActivity.this, new String[]{android
                .Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);


在權限獲取結果中處理文件夾的創建管理:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //創建文件夾
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        File file = new File(Environment.getExternalStorageDirectory().toString() + "/camera/demo/");
                        if (!file.exists()) {
                            Log.d("fileCreate", "file create:" + file.mkdirs());
                        }
                    }
                    break;
                }
        }
    }


3,有些手機有虛擬導航,爲保持頁面的一致,需要管控頁面。

    @SuppressLint("NewApi")
    private void hideVirtualGuideBar() {
        //隱藏不顯示
        Window window = getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE;
        window.setAttributes(params);
    }


因爲需求的關係,獲取到的圖片,還需要做二值化。
且聽下回分解。




傳送門 -- 源碼哦



偶爾會依舊很迷茫,偶爾會找不到自身的定位。總是在跌跌撞撞中找尋。。。


































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