2020年3月8日 星期日

【不要仰賴Activity的Android開發】什麼事情都用CustomView解決





一般來說,製作一個這樣的介面會使用一組Activity和XML。



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
tools:context=".MainActivity"
tools:showIn="@layout/activity_main"> 

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" /> 

 <EditText
android:id="@+id/identify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="10"
android:maxLines="1"
android:hint="ID" />
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="10"
android:maxLines="1"
android:hint="Password" />
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK"/>
</LinearLayout>



public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { 
 super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); 

 final EditText id = findViewById(R.id.identify);
final EditText pw = findViewById(R.id.password); findViewById(R.id.login).setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) { 

 Log.d("Hsu", "ID:" + id.getEditableText().toString());
 Log.d("Hsu", "PW:" + pw.getEditableText().toString()); 


 }
); 
 } 
}
(因為不需要,所以OnClickListener的內容只有意思性地Log。)


接下來的做法或許會比較麻煩,但概念上來說會提供程式比較多的擴充升級彈性。

因為它完全不受限於Activity,功能和介面可以隨時彈性重組,方便調整或擴充。

首先,整個Activity會變成只剩下這樣.......


public class MainActivity extends AppCompatActivity { 
 @Override 
 protected void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main); 

 } 
}

但XML會做出比較大幅度的改變...

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"> 

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" /> 

<com.myapplication.InputText
android:id="@+id/identify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="10"
android:maxLines="1"
android:hint="ID" /> 

<com.myapplication.InputText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="10"
android:maxLines="1"
android:hint="Password" /> 

<com.myapplication.LoginButton
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK"/> 

</LinearLayout>


可以看到EditText和Button從Android標準元件變成APP專案客制的元件。

「com.myapplication」是專案的設定,有興趣的人可以自己彈性的修改命名,只要是「InputText」和「LoginButton」的路徑位置就好。(這兩個View物件的命名也可以自己彈性調整。)

InputText和LoginButton的內容則在下面。

public class InputText extends EditText { 
 public static InputText ID; 
 public static InputText PW; 

 public InputText(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 String hint = getHint().toString();
 switch (hint){ 

 case "ID": ID = this;
 break;
 case "Password": 

 PW = this;
 break;
 } 

 } 
}



public class LoginButton extends Button implements View.OnClickListener { 
 public LoginButton(Context context, AttributeSet attrs) { 
 super(context, attrs);
 setOnClickListener(this);
 } 


 @Override
 public void onClick(View view) { 

 Log.d("Hsu", "ID:" + InputText.ID.getEditableText().toString());
 Log.d("Hsu", "PW:" + InputText.PW.getEditableText().toString()); 

 } 
}


因為這個介面還是算簡單的,所以這樣做的好處可能無法立刻顯現,要引入一些情境做說明。

這樣做的目的在於快速方便的大量產生擁有登入功能的介面。
登入這件事情越來越複雜,會產生各種登入模式,例如多帳號登入、例如彈出視窗登入、例如重新檢查使用者帳號密碼.....
所以快速的設計跟新增登入功能或許有其必要。

InputText可以確保針對登入的輸入功能可以快速重複利用,譬如「將輸入的內容加密」「將使用者帳號自動帶入ID欄」。
而LoginButton可以延生出各種版本,例如CheckPasswordButton可以用來讓使用者再次輸入密碼做保險性登入,等物件設計好後只要修改XML,將LoginButton換成CheckPasswordButton,一樣不需要修改Activity的任何內容,就可以完成新介面新功能。

用這種方式寫程式,為的是可以同時滿足快速組合出功能並達到模組化的目的。


有些人可以從附上的程式碼中快速找到一些功能上的限制,但其實這些限制都可以藉由簡單幾行程式碼擴充來破解,但以後再寫了。