使用基本的Android構建體構建最小可行的產品骰子Android應用。

VectorRelativeLayout , ConstraintLayout , LinearLayout ]ListView和ArrayAdapter列表ListView和Adapter邏輯不久前,我進入了這個瘋狂的棋盤遊戲大廈。該遊戲有點像經典的線索,玩家在房子周圍漫遊,試圖解決一些謎。我強烈推薦這是一款很棒的遊戲。無論如何,遊戲使用骰子擲骰來解決動作和其他遊戲事件。奇怪的是,玩家有時必須比遊戲包括(6)更多的骰子擲骰子!我認為這是構建自定義骰子輥應用程序的絕佳機會。在本教程中,我將使用基本的Android組件來建造瘋狂骰子滾筒的豪宅。

這個骰子應用是專門為瘋狂遊戲玩法的豪宅而設計的,因此首先我將概述遊戲如何使用骰子。
為了保持簡單,該應用將是垂直滾動的骰子列表。該應用程序將具有3個按鈕來觸發功能“滾動骰子”,“添加骰子”和“刪除骰子”。每個骰子將具有相應的“持有”和“更改”。

Android平台總是在變化,隨著時間的流逝,這些教程過時了。作為參考,我的開發環境:
Android環境


在項目創建後,所有這些名稱都可以更改,儘管如果項目變得複雜,它可能會很麻煩地追逐所有名稱引用。

Android有許多版本。每個版本都會更改平台。這基本上意味著世界上有很多具有不同版本的Android設備。這對於應用程序開發人員來說是頭痛,因為根據應用程序使用的庫,該應用可能與某些設備不相容。這裡的權衡是使用新Android庫的應用程序無法在舊設備上運行。如果該應用必須在較舊的設備上運行,則該應用必須使用一些較舊的Android構造。

對於此應用程序,最初的模板實際上並不重要。模板代碼有時很有用,因為它使用某些代碼在佈局和初始類中進行了預言。由於我不會使用任何這些,所以我選擇了空的活動。

Android Project創建對話框用基本構造來初始化項目:
Gradle是一個促進建築項目的框架。在維基百科
Android清單將該應用描述為Android。應用屬性,例如權限和激活物。詳細信息可以在Android文檔頁面上找到。
首先,我使用一個名為Clker的簡單在線SVG編輯器作為SVG繪製骰子麵。



接下來,我使用Android Studio的資產工作室將它們導入我的項目。


設備具有不同的分辨率和尺寸。很難預測應用程序運行的設備的分辨率和尺寸。縮放JPEG會導致模糊或粒狀圖形。解決此問題的一種方法是使用SVG。在線討論SVG的文章有許多文章,但請參考,請查看Wikipedia SVG文章以獲取更多信息。
Android提供了兩種描述佈局的方法:程序化和XML佈局。編程描述複雜的佈局非常困難,因此大多數人通常避免這種情況。編寫XML可能並不是那麼有趣,但是Android Studio確實提供了幾種減輕痛苦的工具。預覽工具和Wsywig佈局編輯器。但是,即使有編輯,潛入XML也幾乎是不可避免的。


Android Studio空的活動模板從ConstraintLayout root佈局元素開始。我們在此應用中需要兩個組件:骰子區域和控制器區域。骰子區域將是可滾動的骰子列表,控制器將是3個按鈕“添加”“刪除”“滾動”。

activity_main.xml <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".MainActivity">
<ListView
android:id="@+id/dice_list"
android:layout_height="0dp"
android:layout_width="match_parent"
app:layout_constraintBottom_toTopOf="@id/button_bar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</ListView>
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/control_bar_height"
android:orientation="horizontal"
android:weightSum="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/dice_list">
<Button
android:id="@+id/add_dice_button"
android:layout_gravity = "center"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_button_label"
android:onClick="addDice"/>
<Button
android:id="@+id/rem_dice_button"
android:layout_gravity = "center"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rem_button_label"
android:onClick="removeDice"/>
<Button
android:id="@+id/roll_dice_button"
android:layout_weight="1"
android:layout_gravity = "center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/roll_button_label"
android:onClick="rollDice"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
對於那些不熟悉XML的人,以上可能看起來像Gibberish。解釋XML不在本教程的範圍之內,但是Google,YouTube和Wikipedia是尋找更多信息的人的絕佳資源。對於這種佈局,我正在使用ListView , Button , LinearLayout和ConstraintLayout 。有關其屬性的詳細信息可以在Android文檔頁面上找到。
我正在尋找一個Layout ,該佈局很容易描述固定的高度底部區域(用於按鈕)和一個填充可用屏幕空間的頂部區域(用於骰子)。使用權LinearLayout使其子元素排出其子元素,使其不合適。 RelativeLayout不能提供“填充剩餘空間”的能力,也使其不合適。
從視覺上講, LinearLayout看起來非常接近我的需求。但是, LinearLayout用於靜態列表,而不是動態列表。對於此應用程序,骰子列表可以在0到25個骰子之間,使ListView是一個更好的候選人。
0dp值是特定於ConstraintLayout的,這表明它應該填充父的剩餘空間。
如上所述,每個骰子行包括2個按鈕和一個骰子圖像。

row.xml <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/row_height">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="@dimen/row_height"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:padding="@dimen/row_padding">
<Button
android:id="@+id/dice_change_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/change_button_label">
</Button>
</FrameLayout>
<ImageView
android:id="@+id/dice_icon"
android:layout_centerInParent="true"
android:layout_width="@dimen/image_width"
android:layout_height="@dimen/image_height"
android:src="@drawable/blank_dice"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="@dimen/row_height"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:padding="@dimen/row_padding">
<Button
android:id="@+id/dice_hold_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hold_button_label">
</Button>
</FrameLayout>
</RelativeLayout>
按鈕位於行的垂直中心,距每個邊緣一定距離。我覺得,如果設計將實際按鈕元素分開及其在佈局中的位置,則該設計將更加干淨。因此,對於我的應用程序,我使用FrameLayout來指定位置並將按鈕中心在該佈局中。
字符串和尺寸值允許我們不將配置字符串和整數直接寫入代碼。對於我們的小應用程序,也許沒什麼大不了的。
string.xml <resources>
<string name="app_name">DiceRoller</string>
<string name="hold_button_label">Hold</string>
<string name="change_button_label">Change</string>
<string name="add_button_label">ADD</string>
<string name="rem_button_label">REM</string>
<string name="roll_button_label">ROLL</string>
</resources>
dimens.xml <?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="row_height">72dp</dimen>
<dimen name="row_padding">16dp</dimen>
<dimen name="control_bar_height">72dp</dimen>
<dimen name="image_width">72dp</dimen>
<dimen name="image_height">72dp</dimen>
</resources>
ListView和ArrayAdapter在這一點上,我已經用空的主動脈初始化了我們的Android項目,並嘲笑了一些佈局。接下來,我將進入邏輯和代碼。首先,我想參加更多特定於Android的Java課程。 ListView是用於渲染視覺列表的基本佈局類。 Android框架通過採用適配器模式將視覺組件( ListView )和數據組件( List<Dice> )分開。在我們的情況下,適配器所做的就是將數據( Dice )映射到某些視覺佈局( dice_row.xml )。在這種情況下,佈局XML文件描述了佈局。
MainActivity.java public class MainActivity extends AppCompatActivity {
DiceAdapter diceAdapter;
List <Dice> diceList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//associating activity to layout
setContentView(R.layout.activity_main);
//Setup ListView and Adapter
ListView listView = findViewById(R.id.dice_list);
diceAdapter = new DiceAdapter(this, R.layout.dice_row, diceList);
listView.setAdapter(diceAdapter);
//Initialize Data
diceAdapter.add(new Dice());
}
public class DiceAdapter extends ArrayAdapter<Dice> {
public DiceAdapter(@NonNull Context context, int resource, List<Dice> list) {
super(context, resource, list);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.dice_row, parent, false);
}
return convertView;
}
}
}
該應用將用骰子對象表示骰子狀態。骰子對象具有兩種屬性:骰子值[空白,放大,恆星],以及骰子是否被“持有”。從功能上講,骰子的擲骰方法將隨機選擇骰子麵。最後,我添加了一種將骰子值更改為列表中下一個方法的方法。
MainActivity.java ....
public static class Dice {
public enum Face {
BLANK,
MAGNIFY,
STAR
}
public static Random random = new Random();
boolean hold = false;
Face diceVal;
Dice() {
roll();
}
public void roll() {
int num = random.nextInt(4);
if(num == 0) { //25% magify
this.diceVal = Face.MAGNIFY;
} else {
//37.5% star, 37.5% blank
if(random.nextBoolean()) {
this.diceVal = Face.BLANK;
} else {
this.diceVal = Face.STAR;
}
}
}
public void toggleHold() {
hold = !hold;
}
public void nextValue() {
int index = diceVal.ordinal();
index = (index+1) % Face.values().length;
diceVal = Face.values()[index];
}
}
在此步驟中,i映射按鈕點擊邏輯。 Android平台提供了幾種做到這一點的方法。一種方法是從佈局文件指定屬性。另一個是編程設置onClickListener 。在我們的應用程序中,對三個頂級按鈕使用屬性方法,並通過編程方式設置行按鈕的偵聽器。
addDice如果骰子計數小於25,則將新的骰子對象添加到骰子列表中。
設計
activity_main.xml ....
<Button
android:id="@+id/add_dice_button"
android:layout_gravity = "center"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_button_label"
android:onClick="addDice"/>
....
邏輯
MainActivity.java ....
public void addDice(View view) {
if(diceList.size()< MAX_DICE_COUNT) {
diceAdapter.add(new Dice());
}
}
....
removeDice骰子列表沒有空,請從列表中刪除最後的骰子
設計
activity_main.xml ....
<Button
android:id="@+id/rem_dice_button"
android:layout_gravity = "center"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rem_button_label"
android:onClick="removeDice"/>
....
邏輯
MainActivity.java ....
public void removeDice(View view) {
if(!diceList.isEmpty()) {
int lastIndex = diceList.size() - 1;
diceAdapter.remove(diceAdapter.getItem(lastIndex));
}
}
....
rollDice重新重新列出了He List上每個骰子的價值,這些價值尚未標記為持有。
設計
activity_main.xml ....
<Button
android:id="@+id/roll_dice_button"
android:layout_weight="1"
android:layout_gravity = "center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/roll_button_label"
android:onClick="rollDice"/>
....
邏輯
MainActivity.java ....
public void rollDice(View view) {
//roll all dice
for(Dice dice : diceList) {
if(!dice.hold)
dice.roll();
}
//notify adapter to update view
diceAdapter.notifyDataSetChanged();
}
....
notifyDataSetChanged ?滾動按鈕更改了相應的骰子對象的置換值。由於視圖/數據適配器模式分離,除非觸發,否則骰子行佈局不會自動重新渲染。呼叫notifyDataSetChanged redraws視圖。
單擊按鈕將設置骰子的持有標誌。
MainActivity.java ....
Button holdButton = convertView.findViewById(R.id.dice_hold_button);
holdButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Dice dice = diceList.get(position);
dice.toggleHold();
}
});
....
單擊“保持”按鈕將更改骰子的值並更新接口。
MainActivity.java ....
Button changeButton = convertView.findViewById(R.id.dice_change_button);
changeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Dice dice = diceList.get(position);
dice.nextValue();
diceAdapter.notifyDataSetChanged();
}
});
....