新鮮人進入職場總是會充滿幹勁,然後希望能夠一步一步克服重重難關突破自己的極限,但是怎麼做的事情越來越偏移當初面試提到的工作內容,而且還是需要寫之前比較少接觸的程式語言....
昨天的文章提到的推薦套件和開發環境怎麼跟Android沒有太多的關聯,因為在執行多個專案與Android關係的就只有一個。因此這一篇會濃縮分享跟當初面試提到與工作內容有關的專案,後續的系列文章基本上不會看到跟Android有關的事情和技術內容。
看到這會想說哇賽那這樣是不是系列文章差不多要結束了,不過人生有趣的地方就是在一些時間點會有驚奇和意料之外的事情發生,因此也沒有因為這樣的情況就馬上開104去尋找下一份工作xD。
這個專案合作的對象是印刷背景的廠商,而該廠商的需求就是希望透過影像辨識的方式去檢驗印刷出來的防偽標籤品質,而辨識的型態包含照片以及即時辨識。接著在分工的部分主要就是從無到有建立一個App,並且去開發KPI有列出來的功能,在開發前需要分幾個面向確認多個功能實現的方法以及可以參考的來源。
在開發Android的APP所使用的IDE可以使用Android Studio進行開發,另外在電腦配備的部分如果是使用模擬器檢視開發後的功能與畫面,建議記憶體起碼也要8G比較好(以前筆電只有8G的時候還被研習的同事嫌怎麼跑那麼慢xD)。當第一次安裝好之後如果要啟用模擬器可以點選畫面右上角的AVD Manage,接著選擇要測試的版本以及虛擬機型號安裝後再點擊三角型開始的符號確定有沒有正常啟動。
當時在畫面的呈現詢問了同事有沒有參考的版本,於是它就開啟APP inventor跟我說它之前設計的初版,當時我看到後也得知之前的計畫跟APP有關的都是用這個工具開發,然後內心默默接受原來部門沒有人知道怎麼使用Android Studio開發,而且如果我開發遇到的問題也沒有人可以問...
但怎麼可以這樣就放棄了呢?
於是確認了基本功能會對應到的畫面後,為了能夠響應不同螢幕大小的裝置首選的layout使用了ConstraintLayout,講到這裡想到大學時如果有推出這個拉畫面的痛苦感可以減少一點了QQ。然而在拉畫面的過程中除了使用UI的畫面拉取物件排版之外,自己的習慣是再使用程式碼確認每個物件參數的設定。
設計畫面時可以用split的方式確認物件的位置相關參數
接著則是物件設計後需要有文字的顯示,而過去常常會直接在android:text後面直接加文字,但如果畫面的文字未來假如需要更換這些字串,除了很耗時間去找到不同類別內的字串外,在會因為打錯字而出現不必要的bug。所以說將字串的定義抽離統一放在strings.xml內,後續維護可以集中修改xml檔案就可以了。
接著分享過去寫的一些功能(久違的開啟之前寫的程式碼回憶一下),由於照片讀取後辨識的那些行為比較機密因此未來如果還有一頭栽進Android的世界也許可以再來分享一下。
備註:下述的範例程式碼為部分擷取,若有變數上的缺漏或者是結構上問題系列文章結束有時間會再更新。
private void ScalePic(Bitmap bitmap, int phone) {
// 縮放比例預設為1
float mScale = 1;
// 如果圖片寬度大於手機寬度則進行縮放,否則直接將圖片放入ImageView內
if (bitmap.getWidth() > phone) {
//判斷縮放比例
mScale = (float) phone / (float) bitmap.getWidth();
Matrix mMat = new Matrix();
mMat.setScale(mScale, mScale);
Bitmap mScaleBitmap = Bitmap.createBitmap(bitmap, 0,
0,
bitmap.getWidth(),
bitmap.getHeight(),
mMat,
false);
imageview.setImageBitmap(mScaleBitmap);
}
// 照片顯示於手機上
else imageview.setImageBitmap(bitmap);
}
那時候測試拍照後的照片顯示到手機時,測試機的螢幕實在太小因此後續才會有這個的判斷行為
// 取得選取相片的檔案路徑
public String getPath(Uri uri) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
取得相簿內照片路徑的行為
// 畫面延遲載入至首頁
final Intent intent = new Intent(this, MainPageActivity.class);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
startActivity(intent);
}
};
timer.schedule(task, 2000);
這個是當時想要做畫面載入等待的效果xD
private void openImageChooserActivity() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
}
//webview呼叫使用圖片上傳參數
private ValueCallback<Uri> uploadMessage;
private ValueCallback<Uri[]> uploadMessageAboveL;
private final static int FILE_CHOOSER_RESULT_CODE = 10000;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//判斷webview選取圖片後回傳的行為
if (requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == uploadMessage && null == uploadMessageAboveL) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (uploadMessageAboveL != null) {
onActivityResultAboveL(requestCode, resultCode, data);
} else if (uploadMessage != null) {
uploadMessage.onReceiveValue(result);
uploadMessage = null;
}
}
if (requestCode == 1 && resultCode == RESULT_OK) {
selectedImageUri = data.getData();
imagepath = getPath(selectedImageUri);
File f = new File(ImageSelectUtils.getRealPath(selectedImageUri, getApplicationContext()));
Bitmap bitmap = BitmapFactory.decodeFile(imagepath);
int bitmap_width = bitmap.getWidth();
int bitmap_height = bitmap.getHeight();
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
if (bitmap_width <= screenWidth) {
imageview.setImageBitmap(bitmap);
} else {
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, screenWidth, bitmap_height * screenWidth / bitmap_width, true);
imageview.setImageBitmap(bmp);
}
messageText.setText("Uploading file path:" + imagepath);
}
super.onActivityResult(requestCode, resultCode, data);
}
另外像是Activity間傳遞和接收結果的使用startActivityForResult與onActivityResult的行為
當時功能寫到卡住一個地方卡太久真的很痛苦
執行這個專案的過程完全沒有可以參考的程式碼(App inventor的功能大多都是畫面單純切換的功能),雖然同事有把OpenCV的相關書籍給我參考但那時候每天都滿滿的疑惑(影像處理的知識非常薄弱),光是確認照片拍完後與同事寫的影像判斷行為串接一起花費蠻大的功夫。除此之外由於同事的專長語言是python,因此開發過程中如果有Java語法上的問題完全沒辦法開話題,只能透過錯誤訊息的關鍵字以及問題可能的方向一步一步進行測試,幸好最後有把廠商需要的功能完成並且順利結案。
雖然工作到現在只有這個專案跟當時的職缺名稱有相關,但寫著寫著自己的內心漸漸的沒有那麼堅持著一定要做跟之前實習有關的相關內容,反而更想接觸新的程式語言或者是頗具挑戰的專案。不過也因為工作的環境又思考著沒有前輩帶領,以及沒有像小組方式開發的限制條件下,自己真的可以依照自己的想法持續下去嗎?
另外如果有想要練習Kotlin語言開發的大家可以去參加國內辦的一些課程(參賽完還會有獎品 讚 !)
https://events.withgoogle.com/android-study-jam-twhk-2021/#content