分類
Web 前端

Bootstrap.v3.Datetimepicker 簡單紀錄

因為BootStrap預設沒有日期或時間的UI元件,搜尋一下發現其他開發者提供的好用外掛

Bootstrap.v3.Datetimepicker!!


首先我的環境是ASP.NET和最原始的WebForm專案

1.安裝Bootstrap.v3.Datetimepicker

直接使用NuGet套件管理員安裝,如下

2.使用 Bootstrap.v3.Datetimepicker

2.1在Site.Master的 <head> 區塊輸入


<link rel="stylesheet" href="Content/bootstrap-datetimepicker.css" />

2.2在Site.Master 的<body> 區塊輸入


<script src="Scripts/jquery-3.4.1.min.js"></script>
<script src="Scripts/moment.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="Scripts/bootstrap-datetimepicker.js"></script>

3. Bootstrap.v3.Datetimepicker 的宣告和使用

Datetimepicker可同時使用日期和時間,也可以單獨使用日期或時間。

3.1 同時使用日期和時間


<div class="container">
   <div class="row">
      <div class='col-sm-6'>
         <div class="form-group">
            <div class='input-group date' id='datetimepicker1'>
               <input type='text' class="form-control" />
               <span class="input-group-addon">
               <span class="glyphicon glyphicon-calendar"></span>
               </span>
            </div>
         </div>
      </div>
      <script type="text/javascript">
         $(function () {
             $('#datetimepicker1').datetimepicker();
         });
      </script>
   </div>
</div>

畫面如下

日期和時間

3.2 只有時間


    <div class="container">
        <div class="row">
            <div class='col-sm-3'>
                <div class="form-group">
                    <div class='input-group date' id='dateAndtime'>
                        <input type='text' class="form-control" />
                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-time"></span>
                        </span>
                    </div>
                </div>
            </div>
            <script type="text/javascript">
                $(function () {
                    $('#dateAndtime').datetimepicker({
                        format: 'LT'
                    });
                });
            </script>
        </div>
    </div>

畫面如下

只有時間

3.3 只有日期


    <div class="container">
        <div class="row">
            <div class='col-sm-3'>
                <div class="form-group">
                    <div class='input-group date' id='dateAndtime'>
                        <input type='text' class="form-control" />
                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-calendar"></span>
                        </span>
                    </div>
                </div>
            </div>
            <script type="text/javascript">
                $(function () {
                    $('#dateAndtime').datetimepicker({
                    format: 'DD/MM/YYYY'
                    });
                });
            </script>
        </div>
    </div>

只有日期

可以看到差異只在選擇哪個icon和在js中呼叫datetimepicker的格式

參考官網 https://getdatepicker.com/4/

分類
git 錯誤紀錄

[GitLab Issue] You are not allowed to push code to protected branches on this project

Scenario

Local 端在 master branch 上執行 git push origin master 後出現以下 Error

remote: GitLab: You are not allowed to push code to protected branches on this project.
To http://xxx.git
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to ‘http://xxx.git’

Root Cause

關鍵字為
You are not allowed to push code to protected branches on this project

從 Google 得知為 GitLab 預設專案的 master branch 為 Protected,無法直接push commit 到 remote 的 master branch。

Solution

A. 透過GitLab的管理者權限將該專案的protected branch關閉。
GitLab->點擊該傳案->左方最下面的settings -> Repository ->點擊 Protected Branch的Expand -> 點擊 Unproject

B. 先git push 到其他branch上,再到GitLab將該branch merged到master(待測)

分類
Android

Firebase Cloud Messaging 使用紀錄

Firebase cloud messaging(FCM) 是什麼?

FCM 是一個跨平台的訊息傳送機制,提供可靠且免費傳遞訊息的功能。

如何使用 FCM?

在已經建立 Firebase 專案的情況下,啟動並使用 FCM 的過程非常簡單。

Android App 只需要加入一個 Service,Firebase 後臺只需要啟動 FCM 並建立要傳送的訊息,最後透過 Firebase 後台發送訊息,Android App 便可收到該訊息。

以下分為 Android App 端,以及 Firebase 後台端來說明。

首先是 Android App 端。

先加入 FCM 的依賴,在 Module 的 build.gradle 加入以下內容

implementation 'com.google.firebase:firebase-messaging:20.0.0'

接著在 AndroidManifest.xml 宣告 Service 如下,Service 名稱可以自訂,注意 <intent-filter> 的 <action> 的 android:name 的內容為 com.google.firebase.MESSAGING_EVENT

<service
android:enabled="true"
android:exported="true"
android:name=".notification.FirebaseMessagingReceiveService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

接著來實作 Service,如下

package com.codefoxx.firebasedemo1app.notification
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class FirebaseMessagingReceiveService : FirebaseMessagingService() {
    private val TAG = this::class.java.simpleName
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        Log.d(TAG, "receive notification:" + remoteMessage.notification?.body)
    }
    override fun onNewToken(token: String) {
        super.onNewToken(token)
        Log.d(TAG, "onNewToken: " + token)
    }
}

Android App端設定完成!! 這樣便可收到訊息。

接下來是 Firebase 後台端

請先進入 Firebase 專案,進入專案之後如果是第一次使用 FCM 必須先建立網路推播憑證。步驟如下

1.點擊 Settings -> 專案設定
(Firebase 專案頁面左上方有 Project Overview,Project Overview 旁邊有一個齒輪圖示,該圖示就是 Settings)

2.點擊雲端通訊

3.在雲端通訊最下方有網路設定,其中有網路推播憑證,在該區塊中若沒有任何內容,請新增一組金鑰。

接下來就可以建立要發送訊息的內容了。

首先點擊畫面左側欄的 Cloud Messaging,再點擊新增通知就會進入訊息內容設定。

1.通知: 填寫訊息的Title和內容

2.指定目標: 選擇使用者區隔或主題
使用者區隔就是選擇符合的條件,其中的應用程式選擇發送目標 App 的 package name,代表所有安裝該目標 App 的裝置都會收到訊息。可以再點擊後方的且,選擇其他的條件。

主題是一個關鍵字,在 App 中必須先訂閱該主題才會發送到目標裝置。

3.排定時間: 就是發送訊息的時間,預設為現在,若有需求可以選擇其他項目。

以上為必選的訊息設定,以下的設定轉換事件和其他選項為選用,視需求來決定要不要使用。

完成後點擊審查會出現訊息提示內容,再按下發布就會發送訊息了。

分類
Design Principle

Inversion Of Control

IoC 是什麼 ?

IoC 是一種設計原則,主要的用途是降低物件之間的耦合,特別是在建立物件的耦合關係。

為什麼要使用 IoC ?

系統為了運作,其中的物件必定有依賴關係,當系統越大越複雜,這些依賴關係也越來越嚴重。因此希望透過由外界提供(注入)物件而不是主動建立物件的方式來降低耦合關係。


如何使用 IoC ?

IoC 比較常見的方法為 DI (Dependency injection),而 DI 的具體實作有 Setter 注入,建構式注入,介面注入。以下使用範例來描述 DI 的具體實作。

當 Client 依賴於 Server,主動建立物件的方式就是使用 new 關鍵字建立需要的物件,如下在 Client 的建構式中建立了 Server 物件。

public class Client {

private Server mServer;

public Client() {
mServer = new Server();
}
}

Setter 注入就是提供一個公開的方法傳入 Server。

public void setServer(Server server) {
mServer = server;
}

建構式注入則是換成從建構式傳入 Server。

public Client(Server server) {
mServer = server;
}

介面注入會另外建立介面(IServer)並提供方法來傳入 Server。

public interface IServer {

public void setServer(Server server);
}
public class Client implements IServer {

private Server mServer;

@Override
public void setServer(Server server) {
mServer = server;
}
}


Simple Factory Pattern 簡單工廠模式

以上的三種注入方式適用於簡單的依賴情況。
若是產生依賴物件的過程較複雜,可以考慮套用工廠系列模式,一個非常簡單的簡單工廠如下

public class ServerFactory {

public static Server makeServer(){
return new Server();
}
}

而 Client 就從 ServerFactory 來取得 Server

public class Client {

private Server mServer = ServerFactory.makeServer();

}

乍看之下 ServerFactory 似乎只做了很簡單的工作,但這個工廠卻可以隔離產生Server 的變化對 Client 的影響。

首先是產生 Server 的變化
假設在系統中有大量產生 Server 的呼叫點(new Server()),但沒有工廠隔開。萬一因為需求變化導致 Server 的建構式需要改變(多傳入一個參數),就代表系統中所有產生 Server 的呼叫點都要修改。若是使用工廠,便可把變化封裝在工廠中。

第二是測試 Client 的難度
若 Server 本身是重量級物件,建立過程需要一堆繁瑣的過程或元件(如存取資料庫或網路元件等等)。那麼測試 Client 的難度會變得非常高,甚至無法測試。而透過 ServerFactory,可以很簡單的建立 MockServer 來測試 Client 的邏輯。


DI 框架

以上的描述都是屬於自己動手來建立 IoC 機制,這些方式對於簡單的依賴關係是足夠的。

但若情況再複雜一些,比如建立 Server 需要再建立 HttpConnection 物件而 HttpConnection 物件需要再建立 Authorization 物件,Authorization 物件又需要傳入 Account 物件…。

因為這些依賴關係之間層層相依且具有順序,靠手動方式建立 IoC 不僅容易出錯還需要寫出一堆樣板代碼,這種情況就可以考慮透過 DI 框架來自動管理。

分類
Android

View Binding

View Binding 是什麼 ?

View Binding 是一個機制,可以讓開發者更簡單以及更安全的參考佈局檔的 UI 元件。
ref : https://developer.android.com/topic/libraries/view-binding

當 Module 啟動 View Binding 之後,該 Module 內每個佈局檔都會產生一個相對應的 binding class,binding class 會包含其對應的佈局檔中具有 id 的屬性,使用者便可透過 binding class 直接操作佈局檔的 UI 元件,不必再寫 findViewById。

View Binding 用來做什麼 ?

在大部分的情況下,View Binding 用來取代 findViewById。

設定和相依性

注意
1.目前(2019-09-12)只支援 Android Studio 3.6 Canary 11+
2.如果已經使用 data binding,不必再啟動 View Binding

在 Module 的 build.gradle 啟動 View Binding

android {
    ...
    viewBinding {
        enabled = true
    }
}

如果不想為某個佈局檔自動產生 binding class,在該佈局檔中加入以下設定

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

用法

在 Module 中啟動 View Binding 之後,只要執行 make project,便會為該 Module 中每個佈局檔建立 binding class。

binding class 檔案位於
app -> build -> generated -> data_binding_base_class_source_out -> debug -> out -> package name -> databinding
(這個其實不重要,因為不必手動修改它)

binding class 名稱會根據對應的佈局檔延伸,規則是佈局檔+Binding
假設有一個為 activity_view_binding_example.xml 佈局檔,內容如下。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ViewBindingExampleActivity">
    <TextView
        android:id="@+id/view_binding_example_hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/view_binding_example_click_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click me"
        app:layout_constraintLeft_toLeftOf="@+id/view_binding_example_hello_world"
        app:layout_constraintTop_toBottomOf="@+id/view_binding_example_hello_world" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="bottom button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

可以看到其中一個 TextView 和 Button 都有 id,而另一個 Button 沒有 id。
binding class 會自動持有具有 id 屬性的 UI 元件變數,忽略掉沒有 id 屬性的UI 元件。

對應的 binding class 名稱為 ActivityViewBindingExampleBinding。它會持有名為 viewBindingExampleClickMe 變數以及 viewBindingExampleHelloWorld 變數。這兩個變數對應佈局檔的

<TextView
 android:id="@+id/view_binding_example_hello_world"
...
/>
<Button
 android:id="@+id/view_binding_example_click_me"
...
/>

只要將 ViewBinding 初始化之後就可以透過這兩個變數來操作佈局檔的 UI 元件,以下為在 Activity 中使用的範例。

class ViewBindingExampleActivity : AppCompatActivity() {
    private lateinit var mViewBinding: ActivityViewBindingExampleBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mViewBinding = ActivityViewBindingExampleBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)
        initUI()
    }
    private fun initUI() {
        mViewBinding.viewBindingExampleClickMe.setOnClickListener { _ ->
            Toast.makeText(this, "change to NEW hello world!!!", Toast.LENGTH_SHORT).show()
            mViewBinding.viewBindingExampleHelloWorld.text = "new hello world"
        }
    }
}

mViewBinding 就是 binding class 的實體變數。

注意初始化 mViewBinding 的呼叫順序部分。初始化完 mViewBinding 之後呼叫setContentView 方法是傳入 mViewBinding 的 root 方法回傳值。
root 方法其實就是回傳對應佈局檔的根元素,本範例就是 ConstraintLayout。

        mViewBinding = ActivityViewBindingExampleBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)

而在 initUI 方法中就是透過 mViewBinding 操作 UI 元件,不須經過初始化也不用 findViewById。

    private fun initUI() {
        mViewBinding.viewBindingExampleClickMe.setOnClickListener { _ ->
            Toast.makeText(this, "change to NEW hello world!!!", Toast.LENGTH_SHORT).show()
            mViewBinding.viewBindingExampleHelloWorld.text = "new hello world"
        }
    }

優勢

View Binding 透過提供 binding class 的實體來操作 UI 元件,這種方式比 findViewById 多了一些好處(就是不用寫 findViewById!!)。

首先為型態安全,因為不必在意呼叫 findViewById 時轉型的型態是否正確。
第二為 Null 安全,因為不必在意呼叫 findViewById 時指定 UI 元件的 ID。

和 Data Binding 的不同

  • Data Binding 只處理使用<layout>標籤來綁定數據。
  • View Binding 無法處理在佈局檔定義的變數以及表達式。

總結

就目前而言 View Binding 是很簡易的新特性,用途有限,主要用於取代 findViewById,其 binding class 提供的方法也不多。

分類
Android

Android 10 on Pixel 3 使用心得

使用 Pixel 好處就是能在第一時間收到 Android 的更新。

Android Developers 在 Twitter上很早( 2019-09-04 1:24 )就發布消息,沒想到20190904 下午就可以更新了。

可更新消息

這次更新檔不小,足足有 1.3G 多。更新的主要內容有 10 項,分別為

  • Live Caption
  • Smart Reply
  • Sound Amplifier
  • Gesture Navigation
  • Dark theme
  • Privacy Controls
  • Location Controls
  • Security Updates
  • Focus mode
  • Family Link

詳細內容在 https://www.android.com/android-10/ ,以下為總結內容。

Live Caption

可以當作 Youtube 播放字幕的加強版,特別的是在沒有網路連線的情況下也可以進行字幕播放。

目前還未開放(2019-09-05)

Smart Reply

對於目前的操作會提供更聰明的建議動作。

目前還試不出來(2019-09-05)

Sound Amplifier

能夠幫助過濾雜音,更清楚地聽見周遭的聲音。

基本上就是聲音擴大器 App,要啟動該服務需要到 設定 -> 無障礙設定 -> 聲音擴大器 -> 使用服務。

啟動 聲音擴大器 服務

啟動之後在下方主螢幕按鈕的右側應該會出現人形圖案,可以直接點及該圖案來快速關閉/開啟聲音擴大器。接著插上耳機播放音樂或影片,在播放中點擊人形圖案便會暫停播放音樂或影片,會放大周遭聲音。

嚴格說起來應該叫做周圍聲音放大器,因為該功能對於目前播放的聲音完全沒有影響,啟動之後只會暫停目前播放媒體轉而放大耳機或耳機麥克風聽到的內容。

Gesture Navigation

提供 3 個新的手勢導覽操作,必須到開啟 設定 -> 系統 -> 手勢 -> 系統操作機制 ->透過手勢操作系統。 開啟後原本下方的主螢幕按鈕將不再顯示。

開啟 Gesture Navigation

第 1 個為”前往主螢幕”,手勢為從螢幕底部快速向上滑動。
第 2 個為”切換應用程式”,手勢為從螢幕底部向上滑動之後稍微停住再放開。
第 3 個為”返回上一個畫面”,手勢為從左側或右側往另一側滑動。

實際操作起來,”前往主螢幕”和”切換應用程式”這兩個手勢在流暢度和速度上跟前一個版本比較起來沒有太大差別,且手勢的開始位置都是螢幕底部,所以在操作的便利性上也沒有太大的優勢。

反而”切換應用程式”的新手勢在螢幕底部往上滑動之後還需要稍微停頓一下。而前一個版本不需要停頓反而還比較流暢。

另一個需要討論的是”返回上一個畫面”,”返回上一個畫面”的新手勢容易和操作 Navigation Drawer 的手勢搞混。
兩者差別在於”返回上一個畫面”必須從畫面的最邊邊開始操作,而 Navigation Drawer 大約是離畫面最邊邊 一到兩公分開始操作。

操作手勢在畫面最邊邊開始的缺點在於如果保護套高於手機,操作起來會卡卡的。
優點在於只要在畫面左右邊都可以開始操作,前一個版本就只能點擊畫面左下方開始。

另外對於”返回上一個畫面”可以調整其靈敏度,調太高就更容易和 Navigation Drawer 衝突,使用預設值即可。

Dark theme

據說可以放鬆眼睛以及電力,基本上就是看個人喜好而定。
設定之後對於有些應用程式(Photos, Calendar)也會套用 Dark Theme。


Dark Theme Settings
Dark Theme Calendar

Dark Theme Photos


Privacy Controls

可以更自由的控制自己的隱私。

在設定中多了一個隱私的項目,該項目可以調整關於私有資料的設定。

隱私項目設定

Location Controls

可以更自由的控制自己的位置。

在 設定 -> 定位 選項中,可以查看和設定那些 App 使用了定位功能。

定位 選項

Security Updates

現在可以通過 Google Play 發送安全隱私更新。更快速的取得這些更新,讓裝置處於更安全的狀態。

Focus Mode

在專注模式下可以避免其他應用程式的通知打擾。還可以針對單獨的應用程式設定。

目前還未開放(2019-09-05)

Family Link

管理孩子使用裝置的狀況,如使用裝置時間,安裝那些 App,查看位置。

可以使用,但有個很大的限制為目前被監控方(!!)的裝置限定為 Android 專用。

分類
Android

2019 Google I/O Android App 原始碼釋出!!

GitHub: https://github.com/google/iosched
基本的介紹在GitHub上都有描述,以下為重點總結

開發環境相關

1.整個App都是用kotlin寫的
2.Import的方式可以直接從Android Studio的project from version control,也可以使用git command line 來 fork
3.Android Studio 版本為3.4
 

架構相關

1.這次(2019)的App也是用 Android Architecture Components來開發
關於 Android Architecture Component 可以參考這裡
App的架構遵循Guide to app architecture的內容來建立
2.1 邏輯會從Activity和Fragment移出並放到ViewModel中
2.2 透過LiveData觀察資料並使用Data Binding Library將佈局中的UI元件綁定到應用程序的數據來源
3.1 使用Repository Layer來處理數據的操作。App的數據來源來自不同位置
3.2 User data儲存在Cloud FireStore(遠端或本地端的緩存)
3.3 User preferences和Settings儲存在SharedPreferences
3.4 Conference data存儲在遠端,需要的時候可以儲存在記憶體中以供App使用
4.Repository Modules 負責處理所有的數據
5.實作LightWeight Domain Layer,該層位於data layer和presentation layer之間,用來處理UI線程之外的業務邏輯
參考.\*UseCase.kt files under shared/domain for examples
6.使用Navigation component簡化Activity
7.使用Room搭配Fts4進行全文搜索
8.使用Dagger2進行依賴注入,在很大程度上依靠dagger-android來抽象樣板代碼
9.使用Espresso進行基本測試,使用JUnit和Mockito進行單元測試
 

FireBase 相關

Cloud Firestore
是所有User Data的來源(由使用者加星號或保留的活動)。 Firestore為提供自動同步和無縫管理的離線功能。
Firebase Cloud Functions
允許執行後端代碼。預訂功能在很大程度上取決於與Firestore結合使用的雲功能
Firebase cloud message
可通知app有關服務器上會議數據(Conference data)的更改。
Remote config
幫助管理應用內常量。
.

import & build

目前的環境為Win10,Android Studio 3.4.1
import和build的過程相當順利,沒有出現任何問題需要修改。
直接使用模擬器 run,如下圖

 
接下來就是一步一步分析source code!!

分類
Android

加入 Action View 或 Action Provider 到 App Bar

什麼是 Action View 和 Action Provider?

Action View
是一種Action,在App Bar中提供豐富的功能。如Search Action View可以讓使用者在App Bar中輸入搜尋文字而不必轉換Activity或Fragment。
 
Action Provider
是一種Action,並有客製化介面。一開始顯示為按鈕或menu item。當使用者點擊時,可以用任何方式控制 Action 的行為。
Android support library提供專門的Action View和Action Provider。 例如SearchView用於輸入搜索查詢,ShareActionProvider用於與其他應用程序共享的操作。也可以定義自己的Action View和 Action Provider。
 

加入 Action View

在 app/src /main/res目錄建立 menu 資源文件,並在其中新增<item>元素。<item>元素中加入以下屬性
actionViewClass:實作該action的類別。
actionLayout:佈局資源檔用來描述該action的外觀。
設定showAsAction屬性為“ifRoom | collapseActionView”或“never | collapseActionView”。
collapseActionView用來描述當使用者未互動時如何顯示。如果Action位於App Bar上顯示為icon。如果Action位於overflow menu,則顯示為menu item。當使用者和Action交互時,會填滿App Bar。如下在App Bar中加入 Search View
/app/src/main/res/menu/action_view_actions.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <item android:id="@+id/action_search"
    android:title="search action"
    android:icon="@android:drawable/ic_menu_search"
    app:showAsAction="ifRoom|collapseActionView"
    app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>

若使用者未和Action互動時,會顯示為android:icon。若App Bar空間不足則顯示在overflow menu。
如果需要調整Action,可以在Activity的onCreateOptionsMenu方法實作。 可以通過呼叫getActionView方法獲取Action View的參考。 例如,以下為取得SearchView的參考

public class ActionViewActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_action_view);
    Toolbar toolBar = findViewById(R.id.app_toolbar);
    setSupportActionBar(toolBar);
  }
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.action_view_actions, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) searchItem.getActionView();
    // Configure the search info and add any event listeners...
    return super.onCreateOptionsMenu(menu);
  }
}

 
啟動App 可以看到App Bar已經加入Search View。
點擊放大鏡icon會出現搜尋框讓使用者輸入內容。

關於如何更進一步的使用SearchView參考這裡https://developer.android.com/guide/topics/search/index.html
完整程式碼如下
/app/src/main/res/layout/activity_action_view.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  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"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".action_view_and_action_provider.ActionViewActivity">
  <include
    android:id="@+id/app_toolbar"
    layout = "@layout/toolbar_main"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

/app/src/main/res/manu/toolbar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>

 

加入 Action Provider

在app/src /main/res目錄建立menu 資源文件,並在其中新增<item>元素。<item>元素中加入actionProviderClass屬性並對該屬性設定action provider完整路徑。如
androidx.appcompat.widget.ShareActionProvider
以下新增SharedActionProvider
app/src/main/res/menu/action_provider_actions.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <item
    android:id="@+id/action_shared"
    android:title="shared action"
    android:icon="@android:drawable/ic_menu_share"
    app:showAsAction="ifRoom"
    app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider" />
</menu>

因為SharedActionProvider具有自己的icon,因此不必提供android:icon屬性。
 

實作 Action Provider 行為

透過在onCreateOptionsMenu方法內初始化ShareActionProvider,並設定其行為。

public class ActionProviderActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_action_provider);
    Toolbar toolBar = findViewById(R.id.app_toolbar);
    setSupportActionBar(toolBar);
  }
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.action_provider_actions, menu);
    initShareActionProvider(menu);
    return super.onCreateOptionsMenu(menu);
  }
  private void initShareActionProvider(Menu menu) {
    MenuItem shareActionMenuItem = menu.findItem(R.id.action_shared);
    ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareActionMenuItem);
    Intent sendAction = new Intent(Intent.ACTION_SEND);
    sendAction.setType("text/plain");
    sendAction.putExtra(Intent.EXTRA_TEXT, "share action text");
    shareActionProvider.setShareIntent(sendAction);
  }
}

啟動App可以看到一個分享的icon顯示在App Bar上。

點擊分享icon,會顯示要分享的方式。

完整程式碼如下
/app/src/main/res/layout/activity_action_provider.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  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"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".action_view_and_action_provider.ActionProviderActivity">
  <include
    android:id="@+id/app_toolbar"
    layout="@layout/toolbar_main"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

/app/src/main/res/menu/toolbar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>

以上為Action View和Action Provider的範例。
 
以下為App Bar的相關內容
http://34.80.81.192/?p=6149
http://34.80.81.192/?p=6176
http://34.80.81.192/?p=6165

分類
Android

加入 Up Action 到 App Bar

什麼是 Up Action?

Up Action的主要功能是返回其parent activity,使用方式相當簡單只有2個步驟。
1.在AndroidManifest.xml中指定其parent activity
2.在Activity的onCreate取得Action Bar物件並呼叫Action Bar的setDisplayHomeAsUpEnabled(true)來啟動up action。
 

指定 Parent Activity

使用up action必須在AndroidManifest.xml中設定其parent activity是哪個Activity。
這可以透過android:parentActivityName 屬性來設定。
在android 4.1之後使用android:parentActivityName,而android 4.1 之前的版本使用<meta-data>。
如下BasicActionActivity 是 UpActionActivity 的 parent activity
/app/src/main/res/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.codefoxx.toolbarexample">
  <application
...
    <activity
      android:name=".basicaction.UpActionActivity"
      android:parentActivityName=".basicaction.BasicActionActivity">
      <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.codefoxx.toolbarexample.basicaction.BasicActionActivity"
        />
    </activity>
    <activity android:name=".basicaction.BasicActionActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
...

 

啟動 Up Action

可以透過呼叫App Bar的setDisplayHomeAsUpEnabled()方法來啟動Up Action。
一般而言會在Activity建立時呼叫setDisplayHomeAsUpEnabled
如onCreate方法。如下

public class UpActionActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_up_action);
    Toolbar toolBar = findViewById(R.id.app_toolbar);
    setSupportActionBar(toolBar);
    ActionBar supportActionBar = getSupportActionBar();
    supportActionBar.setDisplayHomeAsUpEnabled(true);
  }
}

注意
1.不需要在onOptionitemSelected方法內實作up button的動作,因為up button的動作就是返回其parent activity,不需要另外實作。
2.點擊up action時,要返回的parent activity必須已存在於工作棧中,若不存在則會直接關閉目前的Activity。

完整程式碼如下
/app/activity_up_action.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  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"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".basicaction.UpActionActivity">
  <include
    android:id="@+id/app_toolbar"
    layout="@layout/toolbar_main"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

/app/src/main/res/menu/toolbar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>

以上為加入Up Action 到 App Bar的範例。
 
以下為App Bar的相關內容。
http://34.80.81.192/?p=6149
http://34.80.81.192/?p=6165
http://34.80.81.192/?p=6192

分類
Android

加入 Action 到 App Bar

什麼是 Action?

Action主要提供在該Activity中最重要的操作, Action會加入到 App Bar 中。如下圖的加號。

例如,照片瀏覽App可能會提供Action作為共享按鈕和創建按鈕,而當查看單張照片時可能會提供Action作為裁剪和過濾按鈕。
但App Bar的空間有限,如果Action數量多於App Bar中可以容納的數量,則App Bar會將多餘的Action放在overflow menu。也可以指定Action應該顯示在overflow menu中而不是顯示在App Bar。
 

建立 Action

所有的Action和其他元件都是定義在menu資源中。
首先在app/src/main/res/menu目錄中建立menu resource file。
並在menu resource file內加入一個<item>元素,該元素定義要顯示的Action如下
/app/src/main/res/menu/main_actions.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <item
    android:id="@+id/action_add"
    android:title="Add"
    android:icon="@drawable/add"
    android:orderInCategory="1"
    app:showAsAction="ifRoom"
    />
</menu>

android:id=“@+id/action_add”是為了在程式碼中引用Action。android:icon=“@drawable/add”是該Action顯示在App Bar上的圖示android:orderInCategory=“1” 是該Action顯示在App Bar上的順序,越小越靠近左側
app:showAsAction=“ifRoom” 是該Action的圖示如何顯示的設定,可用的選項如下
ifRoom ->app bar有空間再加入,若空間不夠則放入overflow menu
withText -> 加入標題文字
never -> 總是在overflow menu,不在app bar的空間
always -> 總是放在app bar
以上的屬性的說明可以參考這裡
https://developer.android.com/guide/topics/resources/menu-resource
在編輯menu resource file時可以開啟preview視窗,方便查看目前的頁面。

 

加入 Action 到 App Bar

覆寫Activity的onCreatOptionsMenu方法,並在該方法中指定要使用的menu resource file。

package com.codefoxx.toolbarexample.basicaction;
import android.view.Menu;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import com.codefoxx.toolbarexample.R;
public class BasicActionActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_basic_action);
    Toolbar toolBar = findViewById(R.id.app_toolbar);
    setSupportActionBar(toolBar);
  }
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_actions, menu);
    return super.onCreateOptionsMenu(menu);
  }
}

這裡要注意的是若該Activity使用的是Tool Bar,則必須要先設定Tool Bar,也就是上面的

Toolbar toolBar = findViewById(R.id.app_toolbar);
setSupportActionBar(toolBar);

否則下方的onCreateOptionsMenu會失效。
啟動App,便可以看到Action顯示在App Bar中。

實作 Action 行為

接著實作按下該Action時的行為。
覆寫Activity的onOptionsItemSelected方法,該方法就是使用者點擊App Bar上的 Action會觸發的方法。
在該方法中根據在menu resource file的<item> android:id設定的值來判斷按下了哪個Action。
以下範例很簡單的顯示Toast

package com.codefoxx.toolbarexample.basicaction;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import com.codefoxx.toolbarexample.R;
public class BasicActionActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_basic_action);
    Toolbar toolBar = findViewById(R.id.app_toolbar);
    setSupportActionBar(toolBar);
  }
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_actions, menu);
    return super.onCreateOptionsMenu(menu);
  }
  @Override
  public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    int itemId = item.getItemId();
    switch (itemId) {
      case R.id.action_add:
        Toast.makeText(this, "click add action button", Toast.LENGTH_SHORT).show();
        break;
    }
    return super.onOptionsItemSelected(item);
  }
}

完成,以上便是加入Action到App Bar的範例。
完整程式碼如下
/app/src/main/res/layout/activity_basic_action.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  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"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".basicaction.BasicActionActivity">
  <include
    android:id="@+id/app_toolbar"
    layout="@layout/toolbar_main"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

/app/src/main/res/menu/toolbar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>

 
以下是App Bar的相關內容。
http://34.80.81.192/?p=6149
http://34.80.81.192/?p=6176
http://34.80.81.192/?p=6192