Android——簡易照相機

Android——簡易照相機

實驗名稱:Android多媒體應用-拍照
實驗目的:設計一個簡易照相機
實驗內容:利用Camera類設計一個簡易照相機,通過SurfaceView組件實現取景預覽,拍照保存等功能,拍照保存時按camera+序號.jpg的方式保存照片,序號自動增加,確保拍照保存不會覆蓋前面的照片。
實驗日期:2019年5月

一、實驗原理

該相機程序,需要應用surfaceview組件來預覽攝像頭拍攝到的景物,再使用回調接口surfaceholder.callback監控取景視圖。使用照片服務類Camera實現拍照功能,並通過imageview組件顯示。最後通過onPicturetaken方法,將拍攝的照片保存至手機。編寫getFile和checkFileName方法,用來對文件名進行處理,實現了實驗對於文件名的要求。

二、實驗過程記錄

2.1 佈局文件

爲實現基本的相機程序效果,使用最簡單的線性佈局,通過組建的嵌套生成需要的佈局。首先指定最外層的線性佈局對齊方式爲垂直佈局,之後再編寫textview組件顯示程序的標題,然後嵌套一層水平對齊方式的線性佈局,在其中加入兩個button組件用於防止“拍照”和“退出”按鈕。之後在第一層線性佈局中加入ImageView組件用於相片的顯示,最後加入surfaceView組件用於取景預覽。

2.2 控制文件

2.2.1 實例化對象

在MainActivity.java文件中修改文件,以實現對程序的控制。首先在公共類 “MainActivity” 擴展 “Activity” 之後,添加 “implements SurfaceHolder.Callback” ,實現Callback接口,以處理取景預覽功能。之後,如下圖所示實例化所需的對象,並聲明用於保存圖片文件的路徑。
在這裏插入圖片描述

圖2.1

在上圖2.1中,分別實例化了用於拍照功能的“Camera”對象“mCamera”、用於取景預覽的“SurfaceView”對象“surfaceView”、用於回調圖片參數的“SurfaceHolder”對象“holder”、用於顯示圖片的“ImageView”對象“mImageView”、用於拍照退出的按鈕對象“cameraBtn”和“exitBtn”,以及用於記錄圖片保存路徑的對象“path”。

2.2.2 重載構造函數

對構造函數進行修改,使其實現“關聯佈局文件和控制文件,註冊回調監聽器”的功能。如圖2.2所示:
在這裏插入圖片描述

圖2.2

首先,通過findViewById方法關聯圖1中的相關組件,併爲“cameraBtn”和“exitBtn”分別設置監聽事件,最後設置SurfaceHolder對象的相關內容。創建SurfaceHolder對象“holder”,註冊回調監聽器並設置SurfaceHolder的類型。

2.2.3 編寫mClick類

爲按鈕的監聽事件編寫mClick類,以實現拍照和退出功能。如圖2.3所示:
在這裏插入圖片描述

圖2.3

上圖中,構造了一個繼承於OnClickListener的mClick類。通過判斷點擊按鈕傳入的參數v,可以對拍照和退出進行區分,拍照使用takePicture方法,退出則調用exit()函數,對相機資源進行重置。

2.2.4 編寫jpegCallback類

在圖3編寫的mClick類中,takePicture方法用到了獲取照片事件的回調接口jpegCallback,現在需要對jpegCallback類進行編寫。如圖2.4所示:
在這裏插入圖片描述

圖2.4

在上圖中,編寫了繼承於Camera. PictureCallback的類“jpegCallback” ,通過重載onPictureTaken函數,實現了拍照、顯示圖片以及保存圖片的功能。

2.2.5重載surfaceCreated方法

創建相機對象之後,會默認調用三個構造函數,分別爲surfaceCreated、surfaceChanged和surfaceDestroyed,首先重載surfaceCreated函數,如圖2.5所示。
在這裏插入圖片描述

圖2.5

上圖中,首先釋放掉相機資源,然後開啓攝像頭。之後嘗試進行相機預覽,如果捕獲到預覽錯誤信息,則輸出報錯。其中的ReleaseCamera()函數用來重置相機,釋放相機資源。

2.2.6重載surfaceChanged方法

相機畫面發生變化時,將觸發surfaceChanged方法,如下圖2.6所示重載surfaceChanged方法。
在這裏插入圖片描述

圖2.6

重載的surfaceChanged方法會調用initCamera函數,重新設置預覽的畫面。重置包括預覽畫面的格式、大小、預覽框大小等信息。最後調用startPreview方法開始預覽。

2.2.7重載surfaceDestroyed方法

關閉相機時,會觸發surfaceDestroyed方法,如下圖2.7所示重載surfaceDestroyed方法。
在這裏插入圖片描述

圖2.7

可以看到,重載的surfaceDestroyed方法並沒有實現具體的功能。

2.3添加權限

由於程序中用到了SD卡讀寫和相機的調用,所以需要在程序文件中聲明需要申請的權限。包括SD卡讀取、寫入權限,以及相機調用權限。如下圖2.8所示,在AndroidManifest.xml文件中添加下列信息,以申請權限。
在這裏插入圖片描述

圖2.8

2.4安裝APK測試

由於AndroidStudio模擬器在電腦上經常卡頓,所以我決定使用安卓手機進行測試。首先將編譯好的項目打包成APK文件,然後將該APK文件發送至手機端安裝。安裝截圖如圖2.9所示,可以看到安裝時會提示該APP將獲取的手機權限。
在這裏插入圖片描述

圖2.9

點擊安裝即可完成APK文件的安裝。

三、實驗中存在的問題及解決方案

3.1 預覽傾斜問題及解決過程

安裝完成,運行該APP程序,點擊拍照按鈕,可以看到如圖3.1所示的手機界面。其中上面的小畫面是imageView組件顯示的拍完的照片,下面的大圖片是surfaceView組件顯示的相機預覽照片。
在這裏插入圖片描述

圖3.1

拍攝完成之後,推遲該APP,並在手機自帶的文件管理界面,找到test文件夾(如圖3.2),這是剛剛圖片拍攝之後,程序生成的文件夾,用於保存圖片文件。在test文件夾下,可以看到剛剛生成的camera.jpg文件(如圖3.3所示) ,瀏覽該文件,可以查看之前拍攝的圖片(如圖3.4) 。
在這裏插入圖片描述

圖3.2

在這裏插入圖片描述

圖3.3

在這裏插入圖片描述

圖3.4

查看該圖片,發現拍攝出來的圖片,與正常視角相差了90°,而且在圖片拍攝和預覽過程中,圖片和正常視角都是不相符的,都旋轉了90°。
經過查閱資料發現,主要是由於相機傳感器像素座標信息,與手機顯示屏像素座標不相符導致的。圖3.5展示了兩像素座標的不同之處。
在這裏插入圖片描述

圖3.5

(圖片引用於:https://blog.csdn.net/xx326664162/article/details/53350551)
相機傳感器獲取得到圖像,就確定了這幅圖像每個座標的像素值,但是要顯示到手機屏幕上,就需要按照屏幕的座標系來顯示,於是就需要將相機傳感器的座標系逆時針旋轉90度,才能顯示到屏幕的座標系上。
在安卓程序中,提供了相關的setRotation()方法和setDisplayOrientation()方法。其中,setRotation()方法的作用是將相機傳感器獲取到的位圖座標旋轉一定的角度,而setDisplayOrientation()方法的作用是將預覽時的畫面旋轉一定的角度。通過調用這兩個方法,我們就可以實現圖片的正常拍攝和預覽了。如圖3.6所示,在原有程序的基礎上添加該方法, 再生成APK文件進行測試。
在這裏插入圖片描述

圖3.6

重新安裝APK文件,測試運行,運行結果如圖3.7所示。可以看到,預覽的圖片已經可以正常顯示了。然後再到test文件夾中查看camera.jpg文件(如圖3.8) ,同樣,拍攝的圖片也是可以正常顯示了。
在這裏插入圖片描述

圖3.7

在這裏插入圖片描述

圖3.8

3.2 文件命名問題及解決過程

目前的程序所能實現的功能,只能拍攝一張照片,如果拍攝多張照片,由於文件名相同,後面的文件就會將前面已存在的文件覆蓋掉,很不方便。本次實驗要求的是“拍照保存時按camera+序號.jpg的方式保存照片,序號自動增加”。

3.2.1問題分析

爲了實現這個功能,需要在每次文件保存使,都要考慮我們需要生成的文件名是什麼。如果企圖保存當前程序生成的圖片數量,在關閉APP之後,數據不易保存。所以我想的解決辦法是:每次生成的文件名都需要經歷兩次函數,第一個函數需要返回所需文件夾中的所有文件,第二個函數需要判斷指定的文件在該文件夾中是否存在,如果存在則返回一個我們需要的、正確的文件名。

3.2.2測試程序

① getFile()函數
爲了不破壞原有的安卓程序,我在Eclipse中進行相關函數的編寫和測試。首先編寫第一個函數getFile(),該函數需要輸入文件夾的路徑字符串,輸出該文件夾下的所有文件名。程序文件如圖3.9所示:
在這裏插入圖片描述

圖3.9

該程序,首先通過path參數獲取指定的文件對象,再通過listFiles()方法列出該文件夾下,所有的文件,包括文件夾、普通文件等等,並將其保存在array數組中。之後逐個判斷是否爲普通文件,將文件名保存在list中,最後返回list。
現在,將path指定爲桌面上的android文件夾,調用getFiles方法,查看程序輸出。
圖3.10是android文件夾下的文件信息,圖11是程序輸出的信息。
在這裏插入圖片描述

圖3.10

在這裏插入圖片描述

圖3.11

可以看到,除了臨時文件,普通文件均可以正常輸出。
② checkFileName方法
下面實現checkFileName函數,該方法的作用是:在文件名字符串中,判斷所需要的字符串是否存在,如果存在則返回原文件名;不存在則重新調用,利用index參數,實現文件名的遞增過程。最後返回一個正確的、合理的文件名。
checkFileName方法的實現過程是:首先將傳入的文件名參數根據“.”進行分割,分成“.”之前的字符串+起始索引數字index+“.”字符+“.”之後的字符串,然後判斷整個文件名是否包含於長文件名參數中,如果存在則再次調用該方法,同時起始索引數字加一;如果不存在,就直接返回上面連接好的文件名。
現在將checkFileName方法和getFile進行合併,通過main函數進行調用測試。首先在桌面的android目錄下新建一個mydir目錄,在mydir目錄下新建一個test.txt文件,然後運行java程序進行測試。
在這裏插入圖片描述

圖3.12

上圖3.12展示了當前的mydir目錄以及Java程序的運行結果。由於起始索引參數index設置的是0,而文件夾下只有一個test.txt文件,所以程序返回的結果“test.txt”是正確的。爲了測試的準確性,我在mydir目錄下又新建了一個test0.txt文件,如果程序功能正常,應該是會返回test1.txt。下面運行程序測試:
在這裏插入圖片描述

圖3.13

可以看到,程序運行結果正確。

3.2.3安卓測試

現在可以放心地將代碼移植到AndroidStudio程序中去了。在原有的onPictureTaken()方法的基礎上,增加紅色框中的內容,如圖3.14所示。其含義爲:通過getFile方法查找手機"/sdcard/test"目錄下的所有文件,將結果返回至list中。然後通過調用checkFileName方法,檢查list中是否有符合filename的文件名(filename已在之前定義),checkFileName方法會返回合理的文件名,再通過字符串連接,將完整的路徑保存到path字符串中,作爲完整的文件名路徑。
爲了更清晰的顯示文件保存的路徑和文件名信息,我在佈局文件中增加了一個textview組件,在path生成之後,將path輸出到textview顯示,這樣就會對文件的路徑信息更加直觀。
最後,將getFile和checkFileName方法都移植到java文件中,就可以運行測試了。
在這裏插入圖片描述

圖3.14

同樣重新生成APK文件測試運行,進行多次拍攝,下面是我拍攝第8次時的界面截圖(圖3.15)。
在這裏插入圖片描述

圖3.15

因爲文件名起始索引爲0,所以第8次生成的文件名應該是camera7.jpg,可以從APP界面中看到文件的保存路徑。現在在test文件夾下,會有8張我剛剛拍攝的圖片,命名方式爲camera+i。圖3.16展示了test文件夾下的文件信息,文件的保存也是正常的。
在這裏插入圖片描述

圖3.16

3.3 預覽畫面卡頓問題及解決過程

之前的多次測試中,我發現該程序的一個bug:在每次點擊“拍照“按鈕之後,預覽畫面就會卡住(靜止且沒有響應),而且如果此時再次點擊”拍照“或”退出“按鈕,程序就會意外退出。
經過查閱資料,我瞭解到,該問題是由以下原因造成的:
在執行拍照命令時,會調用camera.takePicture()方法,該方法在執行過程中會調用camera.stopPreview來獲取拍攝幀數據,從而進行數據回調。而調用camera.stopPreview方法,就會暫停相機的預覽,並表現爲預覽畫面卡住。如果此時用戶點擊了按鈕的話,也就會再次調用camera.takepicture方法,由於相機還沒有開始預覽,沒有進行相關進程的初始化,就會出現之前遇到的意外退出問題。
解決的方法也很簡單,既然camera.takePicture () 方法調用了camera.stopPreview來停止預覽,那麼只要在camera.takePicture()方法結束之後,手動調用一次camera.startPreview方法,來開啓相機預覽就可以了。
於是進行添加圖3.17紅框中的部分:
在這裏插入圖片描述

圖3.17

再次生成APK文件進行測試,圖3.18爲測試結果。
在這裏插入圖片描述

圖3.18

這是我拍攝第9張圖片之後的界面。上方的imageview組件顯示的是第9張的圖片,而下面的surfaceView顯示的是此時的預覽畫面。此時如果我連續拍攝,繼續點擊“拍照“按鈕,程序也不會因爲bug而意外退出了。實現了連續拍照的功能。

四、實驗結果

現在,程序已經實現了實驗所要求的功能,我將目前程序所能實現的效果錄製成GIF圖片並進行上傳,程序的效果如下所示:http://47.95.13.239/Study/Android/test.gif

五、源代碼

我已經將整理過的該項目的源代碼上傳到了GitHub:
https://github.com/ZHJ0125/AndroidSimpleCameraApp

5.1佈局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:gravity="center_horizontal"
        android:text="拍照測試"
        android:textSize="20dp"
        tools:context=".MainActivity"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/test"
        android:textSize="15dp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center_horizontal">
        <Button
            android:id="@+id/btn1"
            android:layout_width="110dp"
            android:layout_height="wrap_content"
            android:text="拍照"/>
        <Button
            android:id="@+id/btn2"
            android:layout_width="110dp"
            android:layout_height="wrap_content"
            android:text="退出"/>
    </LinearLayout>
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView1"
        android:layout_gravity="center" />
    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="320dp"
        android:layout_height="240dp"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>

5.2程序控制文件

package zhj.com.simplecamera;

import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Button;
import android.widget.ImageView;
import android.app.Activity;
import android.widget.TextView;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;

public class MainActivity extends Activity implements SurfaceHolder.Callback{
    Camera mCamera = null;
    SurfaceView surfaceView;
    SurfaceHolder holder;
    ImageView mImageView;
    Button cameraBtn, exitBtn;
    TextView textView;
    int i = 0;
    String filename = "camera.jpg";    //圖片文件名
    String path = "";   //圖片保存路徑

    /**
     *  Override the onCreate
     *  function:重載構造函數,關聯佈局文件和控制文件,註冊回調監聽器
     **/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //關聯ID
        mImageView = (ImageView)findViewById(R.id.imageView1);
        cameraBtn = (Button)findViewById(R.id.btn1);
        exitBtn = (Button)findViewById(R.id.btn2);
        cameraBtn.setOnClickListener(new mClick()); //設置監聽事件
        exitBtn.setOnClickListener(new mClick());
        surfaceView = (SurfaceView)findViewById(R.id.surfaceView1);
        textView = (TextView)findViewById(R.id.test);
        //System.out.println("begin to holder...");
        holder = surfaceView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }


    /**
     *  class:mClick
     *  function:設置按鍵監聽事件,對應拍照和退出按鈕
     * */
    class mClick implements OnClickListener{
        @Override
        public void onClick(View v) {
            if (v == cameraBtn){
                mCamera.takePicture(null, null, new jpegCallback());    //拍照
                //surfaceCreated(holder);         //調用構造函數
            }
            else if (v == exitBtn){
                exit(); //退出程序
            }
        }
    }
    void exit(){
        mCamera.release();
        mCamera = null;
    }


    /**
     * class:jpegCallback
     * function:實現拍照和保存圖片功能
     * */
    public class jpegCallback implements PictureCallback{
        @Override
        public void onPictureTaken (byte[] data, Camera camera){
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            ArrayList<String> list = getFile("/sdcard/test");
            path = "/sdcard/test/" + checkFileName(list, filename, 0);
            textView.setText("文件保存路徑:" + path);
            try{
                BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(path));
                bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
                outStream.flush();
                outStream.close();
                mImageView.setImageBitmap(bitmap);  //在ImageView顯示拍照的圖片
                mCamera.startPreview();
            }catch (Exception e){
                Log.e("error", e.getMessage());
            }
        }
    }

    private static ArrayList<String> getFile(String path){   //檢測路徑下所有的文件,並返回文件名列表
        File file = new File(path);       // 獲得指定文件對象
        File[] array = file.listFiles();       // 獲得該文件夾內的所有文件
        ArrayList<String> list = new ArrayList<String>();
        for(int i=0;i<array.length;i++){
            if(array[i].isFile()){       //如果是文件,將文件名保存在列表中
                list.add(array[i].getName());
            }
        }
        return list;   //返回儲存文件名的列表
    }
    private static String checkFileName(ArrayList<String> names,String name,int index) {   //檢測文件是否存在,並返回合理的文件名
        if(names.contains(name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length()))) {
            //System.out.println(name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length())+ "Hello");
            //name.substring(0,name.indexOf("."))  -->  返回"."之前的字符串
            //文件存在,再次調用checkFileName方法
            name = checkFileName(names,name,index+1);
        } else {
            //文件不存在,返回合理的文件名
            return name.substring(0,name.indexOf("."))+index+name.substring(name.indexOf("."),name.length());
        }
        return name;
    }


    /**
     *  Override the surfaceCreated
     *  function:創建相機時觸發,開啓相機預覽功能
     * */
    @Override
    public void surfaceCreated(SurfaceHolder holder){
        if (mCamera != null){
            ReleaseCamera();    //首先釋放相機資源
        }
        mCamera = Camera.open();    //開啓攝像頭
        //System.out.println("\n\n\nCamera.open() is OK !!!\n\n\n");

        //mCamera = android.hardware.Camera.open();
        //mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
        //System.out.println("Camera is OK!");
        try{
            mCamera.setPreviewDisplay(holder);  //設置相機預覽
        }catch (IOException e){
            System.out.println("預覽錯誤");
        }
    }
    private void ReleaseCamera()    //重置相機
    {
        if(mCamera != null)
        {
            mCamera.release();
            mCamera = null;
        }
    }


    /**
     *  Override the surfaceChanged
     *  function:當畫面發生改變時觸發,重置相機參數
     * */
    @Override
    public void surfaceChanged (SurfaceHolder holder, int format, int width, int height){
        //System.out.println("Camera is going to ready...");
        initCamera();   //重置相機參數
    }
    //設置相機參數
    public void initCamera(){
        //System.out.println("here1....");
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPictureFormat(PixelFormat.JPEG);  //照片格式
        parameters.setPreviewSize(320, 240);    //預覽規格大小
        parameters.setPictureSize(320, 240);    //圖片大小
        parameters.setRotation(90);           //設置照片數據旋轉90°
        mCamera.setParameters(parameters);
        mCamera.setDisplayOrientation(90);    //設置瀏覽畫面水平轉90°

        mCamera.startPreview(); //開始預覽
    }


    /**
     *  Override the surfaceDestroyed
     *  function:關閉相機時觸發,空
     * */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder){    }    //消滅相機時觸發

}

六、待解決問題和設想

6.1 待解決問題

①功能單一
該APP還有很多不完善的地方,功能非常單一,只能實現簡單的拍照功能。
②版本兼容問題
本次實驗中所有的APP測試均在安卓7.1.2版本的手機上進行,在新建工程時,我將最底支持版本選擇爲API 14和Android 4.0,但是在Android版本爲4.4.2的手機上測試時,依然會出現很多問題,導致程序異常結束。程序還未在更高Android版本的手機上測試過,尚不清楚將會有什麼bug。版本的兼容問題是亟待解決的關鍵問題之一。
③關於連拍功能
報告中已經說明了連續拍照的實現過程,到目前爲止,程序已經實現該過程:點擊“拍照“按鈕  拍攝照片  imageView顯示照片 surfaceView啓動預覽 循環拍攝。但在調試過程中我發現,如果點擊”拍攝“按鈕過快,可能導致相機預覽camera.startPreview還未開始,就點擊了”拍攝“按鈕,於是就會導致跟之前相同的問題,讓程序意外結束。這也是待解決的問題之一。

6.2 設想

①版本問題
現在版本問題是最嚴重的一個問題,因爲大家使用的Android手機,其Android版本必然不同,如果不能解決該問題,可能導致很多手機無法正常運行該程序,或者會出現很多bug。現在需要查閱一下Android版本的資料,並進行兼容性的改進。
②加功能
現在功能很單一,以後希望能加上攝像等功能,做到比安卓自帶的相機功能更加完善(笑)。

七、實驗總結

整個實驗項目進行的還算順利,一開始還有很多問題,最後基本上都通過查閱資料以及和同學的交流,解決了問題。
問題還算挺多的,主要是一些細節問題導致的,需要對整個工程的結構,以及各個函數的功能都理清楚,才能更好地解決問題。
調試也有很多技巧,最簡便的是使用輸出語句判斷程序的執行,還有就是通過註釋部分功能,判斷出問題的位置等等。

八、部分參考資料

  1. 關於androidcamera相機的方向的理解http://blog.sina.com.cn/s/blog_68f23d9f0102y2cc.html
  2. Java檢測文件名是否重複 https://blog.csdn.net/u014804332/article/details/80385217
  3. JAVA中方法的調用 https://www.imooc.com/article/13423
  4. 預覽卡住解決辦法思路引導
    https://www.jianshu.com/p/586af3a2dc8d?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
  5. Android相機開發和遇到的坑 https://blog.csdn.net/xx326664162/article/details/53350551
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章