這一篇會講解天氣API、計算機、1A2B的寫法,這次天氣Api的部分會使用一種叫MVP的架構撰寫,使用這種架構撰寫程式有助於程式的可讀性,也比較好做修整,簡單介紹一下就是將View(介面)、Presenter(邏輯判斷) 給分開,讓個別代表的東西可以分開來去做設定,運作順序是 透過View去告知 Presenter 要執行什麼,再透過Presenter去告知View有什麼東西更新了,View接收到後再去作變更。
文章最後會附上三個功能的完整程式碼。
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//Retrofit
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'io.reactivex.rxjava3:rxjava:3.1.6'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
//RxJava
<uses-permission android:name="android.permission.INTERNET" />
將spinner的資料加進string,要使用到時在調取
<string-array name="location_data">
<item>宜蘭縣</item>
<item>花蓮縣</item>
<item>臺東縣</item>
<item>澎湖縣</item>
<item>金門縣</item>
<item>連江縣</item>
<item>台北市</item>
<item>新北市</item>
<item>桃園市</item>
<item>臺中市</item>
<item>臺南市</item>
<item>高雄市</item>
<item>基隆市</item>
<item>新竹縣</item>
<item>新竹市</item>
<item>苗栗縣</item>
<item>彰化縣</item>
<item>南投縣</item>
<item>雲林縣</item>
<item>嘉義縣</item>
<item>嘉義市</item>
<item>屏東縣</item>
</string-array>
<string-array name="element_data">
<item>Wx</item>
<item>PoP</item>
<item>MinT</item>
<item>CI</item>
<item>MaxT</item>
<item>All</item>
</string-array>
<string-array name="time_data">
<item>今天</item>
<item>明天</item>
<item>後天</item>
</string-array>
<string-array name="tw_element">
<item>當日天氣氣象 : </item>
<item>當日降雨機率 : </item>
<item>當日最低溫度 : </item>
<item>當日舒適度 : </item>
<item>當日最高溫度 : </item>
</string-array>
首先先建立資料傳輸的媒介,建立一個class叫WeatherResponse
import java.util.List;
public class WeatherResponse {
public Records records;//這次的Api資料,records後面是{},也就是class類別,所以要建立一個records的class
public class Records{//接著看到records底下的location,他後面是先[]才是{},意思是location裡面的資料是用list包起來,再更裡面的資料是class類別
public List<Location> location;//這裡代表在List裡面放入Location的物件,而Location是class,所以就是用List去包class,就可以完成上述的順序
}//下面的資料都是以相同的架構去寫
public class Location{
public String locationName;
public List<WeatherElement> weatherElement;
@Override
public String toString() {
return "Location{" +
"weatherElement=" + weatherElement +
'}';
}
}
public class WeatherElement{
public String elementName;
public List<Time> time;
}
public class Time{
public Parameter parameter;
}
public class Parameter{
public String parameterName;
public String parameterUnit;
}
public String getDataByTime(Integer index,Integer day){
return records.location.get(0).weatherElement.get(index).time.get(day).parameter.parameterName;
}
public String getUnitByTime(Integer index){
return records.location.get(0).weatherElement.get(0).time.get(index).parameter.parameterUnit;
}
public Integer getElementSize(){
return records.location.get(0).weatherElement.size();
}
接著要建立一個class叫ApiClient
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
public Retrofit myWeatherApi(){
return new Retrofit.Builder()
.baseUrl("https://opendata.cwa.gov.tw/api/v1/rest/datastore/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory.create())
.build();
}
}
再來要建立一個interface叫GetApi
import com.example.demo.CommonData.Resopnse.WeatherResponse;
import io.reactivex.rxjava3.core.Observable;
import retrofit2.Response;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GetApi {
@GET("F-C0032-001")
Observable<Response<WeatherResponse>> getWeatherApi(
@Query("Authorization") String Authorization,
@Query("locationName") String locationName,
@Query("elementName") String elementName
);
}
首先建立一個interface,取名為WeatherContract,就是Weather這個功能的接口接著會在裡面放入兩個介面「view
、presenter
」,並且會在這兩個介面之下再去撰寫相關的方法
import com.example.demo.CommonData.Resopnse.WeatherResponse;
public interface WeatherContract {
interface view{
void getWeatherData(WeatherResponse data);
}
interface presenter{
void setWeatherApi(String locationName, String elementName, String time);
}
}
可以看到我在view之下建立了獲取天氣資料的方法,並在presenter之下建立了要取天氣Api資料的方法
接著建立一個class,取名為WeatherPresenter,在這裡要implement WeatherContract的presenter,就是將接口的presenter引用到這裡,並且撰寫相關的方法應該要做的事,下面會一步一步介紹要如何撰寫
public class WeatherPresenter implements WeatherContract.presenter
首先要在presenter先implements Contract定義的presenter
接著如果有在接口的presenter定義方法,那implements後的presenter也會需要定義那些方法的內容。
private WeatherContract.view view;
private ApiClient apiClient;
private GetApi getApi;
public WeatherPresenter(WeatherContract.view view){
this.view = view;
apiClient =new ApiClient();
getApi = apiClient.myWeatherApi().create(GetApi.class);
}
宣告最主要是要定義view,跟建構元搭配將這裡的view跟傳入的view綁定,才可以將做完邏輯處理的資料回傳到正確的view,但是這次因為結合了抓取天氣Api資料的功能,所以就將抓取Api資料的工作也丟到presenter來執行,因此還宣告了ApiClient跟GetApi,
再來建構元的部分,就如第一段提到的要設定傳入view,接著上面宣告的view就要綁定到傳入的view,接著就是其他物件的初始化設定。
@Override
public void setWeatherApi(String locationName, String elementName, String time) {
String authorization = "CWB-2F70211E-8C2F-4A7F-8841-292FDCE00BEB";
if (elementName.equals("All")) elementName = "";
String finalSelectedElement = elementName;
getApi.getWeatherApi(authorization,locationName,finalSelectedElement)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<Response<WeatherResponse>>() {
@Override
public void onNext(@NonNull Response<WeatherResponse> weatherResponse) {
view.getWeatherData(weatherResponse.body());
}
@Override
public void onError(@NonNull Throwable e) {
Log.d("test", "onError: " + e +"\n");
}
@Override
public void onComplete() {
Log.d("test", "onComplete: ");
}
});
}
最後就是方法的撰寫,這裡基本上就跟之前介紹的一樣,只有在傳輸資料時做了點變動,這次我將抓取到的資料全部回傳到view去做處理,這邊寫到view.getWeatherData(...)
,就是在呼叫接口裡面的view的方法,但是因為這裡的view跟呼叫方法那端的view綁定,所以就會將資料回傳給呼叫方法那端的getWeatherData()。
接著就進到activity端也就是view的設定
public class WeatherFunction extends AppCompatActivity implements WeatherContract.view
首先要先做的就是implements 接口的 view,並且將view的方法給引入,在輸入完上面那串後,會發現底下出現紅色的線,這其實就是在提醒你要引入view的方法,一樣的在那個位置按下 ALT + ENTER 就會幫你將方法給引入囉~
private FloatingActionButton floatingActionButton;
private TextView result;
private Button search;
private Spinner location_spinner,element_spinner,time_spinner;
private String selected_location,selected_element,selected_time;
private String[] location_data,element_data,time_data,tw_element;
private WeatherPresenter presenter;
接著就是各種物件的宣告,這裡稍微提一下之前沒出現過的東西private FloatingActionButton floatingActionButton;
,這個是一個叫做浮動式按鈕的物件,這個物件可以無視linearLayout的限制,強行讓它出現在我指定的位置,使用方法就跟一般的Button一模一樣,因此在這次的實作我將這個按鈕設定為返回上一頁的功能private WeatherPresenter presenter;
,這個是呼叫presenter,之後初始化時就回傳入這個activity的view。
presenter= new WeatherPresenter(this);
setFindById();//綁定物件id
setListener();//設定監聽器
getSpinnerData();//獲取Spinner選單的資料
setSpinner();//設定Spinner
物件的初始化以及宣告,這個部分設定spinner的部分就跟之前文章講解的一樣,這裡就不重複說明有需要看詳解的可以看之前的文章Day17,這裡就講解setFindById跟setListener的作用,不過看註解其實也知道這個在做的是什麼,但這邊還是稍微展示一下
private void setFindById() {
floatingActionButton = findViewById(R.id.weather_floatActionButton);
result = findViewById(R.id.weather_result);
search = findViewById(R.id.weather_search);
location_spinner = findViewById(R.id.weather_locationName);
element_spinner = findViewById(R.id.weather_elementName);
time_spinner = findViewById(R.id.weather_time);
}//綁定物件id
private void setListener() {
floatingActionButton.setOnClickListener(view -> finish());
search.setOnClickListener(view -> presenter.setWeatherApi(selected_location,selected_element,selected_time));
}//設定監聽事件
這邊可以看到浮動式按鈕我設定它執行finish的工作,也就是將現在這個頁面結束掉,這樣就會返回到上一頁囉~
接著當我按下搜尋按鈕後,就會呼叫presenter的抓取天氣Api資料的方法,前面也介紹過當抓取完資料後,就會呼叫view的方法將資料回傳到view,所以下面就直接帶到剛剛引入的view的方法吧
@Override
public void getWeatherData(WeatherResponse data) {
if (selected_element.equals("All")) selected_element = "";
String finalSelectedElement = selected_element;
result.setText("");
List time_list = Arrays.asList(time_data);
List element_list = Arrays.asList(element_data);
if(data.getElementSize() != 1){
for (int i = 0; i < data.getElementSize(); i++) {
result.append(tw_element[i] + data.getDataByTime(i,time_list.indexOf(selected_time))+"\n");
}
}
else {
result.setText(tw_element[element_list.indexOf(finalSelectedElement)] + data.getDataByTime(0,time_list.indexOf(selected_time))+"\n");
}
}
當呼叫presenter的抓取天氣Api資料的方法後,就會使用getWeatherData來將資料傳回到view,所以這個方法就會在呼叫完方法後,又被呼叫並且執行,那我就可以將把資料設定到TextView上的工作也放到這裡,讓view接收到資料後就把資料丟給TextView顯示。
計算機的撰寫我就沒使用到MVP架構了,因為也不需要,特地去使用反而會複雜化程式
public class CalculatorFunction extends AppCompatActivity implements View.OnClickListener
首先先implements View.OnClickListener
@Override
public void onClick(View view) {
}//當有點擊事件發生時就集中到這裡處理
接著需要引入onClick
這個方法,這裡會這麼做是為了要將點擊事件都集中到一起做處裡
private Button add, sub, mult, div, dot, equal,clear;
private Button num_0, num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9;
private TextView tv_solution,tv_result;
private FloatingActionButton floatingActionButton;
宣告的部分
setFindById();//綁定物件id
setListener();//設定監聽器
onCreate的內容,這裡綁定物件id跟剛剛介紹的一樣,監聽器的部分則有一點變動
private void setListener() {
add.setOnClickListener(this); sub.setOnClickListener(this);
mult.setOnClickListener(this); div.setOnClickListener(this);
dot.setOnClickListener(this); equal.setOnClickListener(this);
num_0.setOnClickListener(this); num_1.setOnClickListener(this);
num_2.setOnClickListener(this); num_3.setOnClickListener(this);
num_4.setOnClickListener(this); num_5.setOnClickListener(this);
num_6.setOnClickListener(this); num_7.setOnClickListener(this);
num_8.setOnClickListener(this); num_9.setOnClickListener(this);
clear.setOnClickListener(this);
//將點擊事件設定到onClick
floatingActionButton.setOnClickListener(view -> finish());
//浮動式按鈕不用集中到onClick,所以這裡就照樣執行finish的指令
}//設定監聽器
在setOnClickListener裡面輸入this,就可以將點擊事件都集中到onClick設定
@Override
public void onClick(View view) {
Button button = (Button) view;
String btnText = button.getText().toString();
String dataToCalculate = tv_result.getText().toString();
if (btnText.equals("=")){
tv_result.setText(getResult(tv_solution.getText().toString()));
tv_solution.append(dataToCalculate + btnText);
return;
} else if (btnText.equals("CLEAR")) {
tv_solution.setText("");
tv_result.setText("");
return;
} else if (btnText.equals("+") || btnText.equals("-") || btnText.equals("*") || btnText.equals("/")){
tv_solution.setText(tv_result.getText() + btnText);
tv_result.setText("");
}else{
dataToCalculate = dataToCalculate + btnText;
tv_result.setText(dataToCalculate);}
}//當有點擊事件發生時就集中到這裡處理
首先要先定義點擊事件為哪個按鈕執行的Button button = (Button) view;
,
接著因為Button上面的文字就是我們所需要的資料,所以這裡又用了一個String將觸發點擊事件的Button的文字記錄下來,
這次計算機我的想法為,按下數字時下方的螢幕會顯示出來,按下計算符號時就將下方螢幕的資料丟到上面的螢幕紀錄,接著再按下數字跟=鍵進行計算,並把結果丟給下方主螢幕顯示
計算的方法使用到了mxparser這個第三方函式庫對輸入的數學式進行計算,這裡附上下載連結
往下拉到這裡,並點選中間JAVA的那個選項
下載完後解壓縮會得到這個檔案
點開後再按bin會來到這個畫面
這裡要選擇自己android studio使用的jdk版本,如果不確定自己的jdk版本為多少的話,這邊教各位一個方法查看
首先點擊File再點選Project Structure...
接著再找到SDK Location裡面的JDK Location
再來就可以在下面的Gradle JDK找到囉~
這裡可以看到我的JDK是17版,所以我就選擇JDK17的資料夾點進去
接著會看到這個檔案,將它複製起來
接著回到android studio
將Android 改選為 Project
接著 Demo > app > libs,將剛剛複製的檔案貼到libs,直接ctrl+v貼上就可以了
最後對著貼入的的檔按按右鍵,再按Add As Library...
到此就成功將第三方套件給裝好囉~
private String getResult(String dataToCalculate) {
try {
Expression expression = new Expression(dataToCalculate+tv_result.getText().toString());
return String.valueOf(expression.calculate());
}catch (Exception e){
return "Error";
}
}
接著看到內容,使用方法就是將要計算的String傳給Expression,接著再.calculate就會自動幫你計算囉~
這次小遊戲我選擇 1A2B 來製作,這個部分也可以發揮自己的創意做不同的遊戲,這個部分也是一樣是直接撰寫沒有使用MVP架構
private EditText et_player_answer;
private Button btn_enter;
private TextView result;
private FloatingActionButton floatingActionButton;
private ArrayList<Integer> CorrectAnswer, PlayerAnswer;
宣告的部分,這裡用兩個ArrayList來分別儲存「正確答案、玩家答案」
setFindById();//綁定物件id
setAnswer();//設定正確答案
setListener();//設定監聽器
onCreate的部分,在一進到程式後就先產生一個答案,接著就可以開始進行猜題
private void setAnswer() {
CorrectAnswer= new ArrayList<>();
ArrayList<Integer> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add(i);
}//先建立一個0到9的陣列
Collections.shuffle(data);//接著將陣列洗牌
CorrectAnswer.addAll(data.subList(0,4));//將前四個數字加進正確解答
Log.d("test", "setAnswer: "+CorrectAnswer);
}//設定正確答案
我產生正確答案的方法為,先建立一個0到9的陣列,接著使用shuffle的指令,將0~9的資料打亂,最後在把前4個數字丟給正確答案的陣列
private void setListener() {
btn_enter.setOnClickListener(view -> CheckAnswer(et_player_answer.getText().toString()));
floatingActionButton.setOnClickListener(view -> finish());
}
浮動式按鈕的設定還是一樣,再按下輸入按鈕後,會呼叫CheckAnswer並把輸入的資料傳入
PlayerAnswer = new ArrayList<>();
et_player_answer.setText("");//每次輸入完答案都把EditText清空
String regex = ".*[a-zA-Z].*";//使用正則表達式判斷輸入的答案有沒有英文字母
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(player_answer);
if (player_answer.length() != 4 || matcher.matches()) {
Toast.makeText(this,"請輸入四個數字",Toast.LENGTH_SHORT).show(); return;}
首先開頭就先檢測傳入的資料是否符合規定,在檢測英文字母時使用到了正則表達式,接著使用if判斷式判斷輸入的字串長度是否不等於四,以及是否輸入了英文,如果不符合規則就用一個Toast來告知要重新輸入,並且用return;
直接將這段程式結束掉,這個部分可以使用的方法也很多,或許也可以使用字符串來判斷是否輸入正確。
for (int i = 0; i < 4; i++) {
PlayerAnswer.add(Character.getNumericValue(player_answer.charAt(i)));
}//將玩家輸入的答案丟給陣列存取
int quantityA = 0, quantityB = 0;//A跟B的數量
for (int i = 0; i < 4; i++) {
if (CorrectAnswer.contains(PlayerAnswer.get(i))){//先判斷輸入的答案有沒有在正確答案內
if (CorrectAnswer.get(i).equals(PlayerAnswer.get(i))) quantityA++;//如果有就判斷當下的數字是否跟正確答案一樣,是就A+1
else quantityB++;//反之B+1
}
}//判斷幾A幾B
if (quantityA == 4){
Toast.makeText(this,"恭喜~你猜中了!",Toast.LENGTH_SHORT).show();
result.setText("");
setAnswer();
}//當A的數量為四個的時候,就展開新的一輪
result.append("這次輸入 : "+ PlayerAnswer + ",結果為 : " + quantityA + "A" + quantityB + "B\n");
//沒有4A的時候就正常顯示幾A幾B
當輸入的數字符合規定時就進到下一個階段,首先將玩家輸入的答案丟到一個陣列存取
接著用兩層的if判斷式來判斷A跟B的數量,首先先判斷輸入的答案有沒有在正確答案內,有就進到下一個if判斷式,判斷這個數字的位置跟正確解答是否一樣,是就讓A的數量+1反之就讓B的數量+1
最後再進行判斷現在的A跟B的數量,再將結果放到TextView顯示出來
這次的設計TextView不是單純的顯示出來而已,因為考慮到或許會猜太多次,所以這次我將TextView在布局的時放進了ScrollView裡面,在ScrollView裡面的物件如果超過ScrollView原本的大小,就會升成一個滾輪可以上下滑動,所以當猜的次數太多時,就可以上下滑動以確認自己所猜的數字
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.demo.CommonData.Resopnse.WeatherResponse;
import com.example.demo.R;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.Arrays;
import java.util.List;
public class WeatherFunction extends AppCompatActivity implements WeatherContract.view {
private FloatingActionButton floatingActionButton;
private TextView result;
private Button search;
private Spinner location_spinner,element_spinner,time_spinner;
private String selected_location,selected_element,selected_time;
private String[] location_data,element_data,time_data,tw_element;
private WeatherPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather_function);
presenter= new WeatherPresenter(this);
setFindById();//綁定物件id
setListener();//設定監聽器
getSpinnerData();//獲取Spinner選單的資料
setSpinner();//設定Spinner
}
private void setSpinner() {
ArrayAdapter location_adapter = new ArrayAdapter(
this,
android.R.layout.simple_spinner_dropdown_item,
location_data
);
ArrayAdapter element_adapter = new ArrayAdapter(
this,
android.R.layout.simple_spinner_dropdown_item,
element_data
);
ArrayAdapter time_adapter = new ArrayAdapter(
this,
android.R.layout.simple_spinner_dropdown_item,
time_data
);
location_spinner.setAdapter(location_adapter);
element_spinner.setAdapter(element_adapter);
time_spinner.setAdapter(time_adapter);//分別設定spinner的資料
location_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selected_location = location_spinner.getSelectedItem().toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});//location的spinner點擊事件
element_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selected_element = element_spinner.getSelectedItem().toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});//element的spinner點擊事件
time_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selected_time = time_spinner.getSelectedItem().toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {}
});//time的spinner點擊事件
}//跟Spinner相關的設定
private void getSpinnerData() {
location_data = getResources().getStringArray(R.array.location_data);
element_data = getResources().getStringArray(R.array.element_data);
time_data = getResources().getStringArray(R.array.time_data);
tw_element = getResources().getStringArray(R.array.tw_element);
}//從string獲取資料
private void setListener() {
floatingActionButton.setOnClickListener(view -> finish());
search.setOnClickListener(view -> presenter.setWeatherApi(selected_location,selected_element,selected_time));
}//設定監聽事件
private void setFindById() {
floatingActionButton = findViewById(R.id.weather_floatActionButton);
result = findViewById(R.id.weather_result);
search = findViewById(R.id.weather_search);
location_spinner = findViewById(R.id.weather_locationName);
element_spinner = findViewById(R.id.weather_elementName);
time_spinner = findViewById(R.id.weather_time);
}//綁定物件id
@Override
public void getWeatherData(WeatherResponse data) {
if (selected_element.equals("All")) selected_element = "";
String finalSelectedElement = selected_element;
result.setText("");
List time_list = Arrays.asList(time_data);
List element_list = Arrays.asList(element_data);
if(data.getElementSize() != 1){
for (int i = 0; i < data.getElementSize(); i++) {
result.append(tw_element[i] + data.getDataByTime(i,time_list.indexOf(selected_time))+"\n");
}
}
else {
result.setText(tw_element[element_list.indexOf(finalSelectedElement)] + data.getDataByTime(0,time_list.indexOf(selected_time))+"\n");
}
}
}
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.demo.R;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.mariuszgromada.math.mxparser.Expression;
public class CalculatorFunction extends AppCompatActivity implements View.OnClickListener {
private Button add, sub, mult, div, dot, equal,clear;
private Button num_0, num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9;
private TextView tv_solution,tv_result;
private FloatingActionButton floatingActionButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calculator_function);
setFindById();//綁定物件id
setListener();//設定監聽器
}
private void setListener() {
add.setOnClickListener(this); sub.setOnClickListener(this);
mult.setOnClickListener(this); div.setOnClickListener(this);
dot.setOnClickListener(this); equal.setOnClickListener(this);
num_0.setOnClickListener(this); num_1.setOnClickListener(this);
num_2.setOnClickListener(this); num_3.setOnClickListener(this);
num_4.setOnClickListener(this); num_5.setOnClickListener(this);
num_6.setOnClickListener(this); num_7.setOnClickListener(this);
num_8.setOnClickListener(this); num_9.setOnClickListener(this);
clear.setOnClickListener(this);
//將點擊事件設定到OnClick
floatingActionButton.setOnClickListener(view -> finish());
//浮動式按鈕不用集中到OnClick,所以這裡就照樣執行finish的指令
}//設定監聽器
private void setFindById() {
add = findViewById(R.id.btn_add); sub = findViewById(R.id.btn_sub);
mult = findViewById(R.id.btn_mult); div = findViewById(R.id.btn_div);
dot = findViewById(R.id.btn_dot); equal = findViewById(R.id.btn_equal);
num_0 = findViewById(R.id.num_0); num_1 = findViewById(R.id.num_1);
num_2 = findViewById(R.id.num_2); num_3 = findViewById(R.id.num_3);
num_4 = findViewById(R.id.num_4); num_5 = findViewById(R.id.num_5);
num_6 = findViewById(R.id.num_6); num_7 = findViewById(R.id.num_7);
num_8 = findViewById(R.id.num_8); num_9 = findViewById(R.id.num_9);
clear = findViewById(R.id.btn_clear);
floatingActionButton = findViewById(R.id.calculator_floatActionButton);
tv_result = findViewById(R.id.calculator_result);
tv_solution = findViewById(R.id.calculator_solution);
}//綁定物件id
@Override
public void onClick(View view) {
Button button = (Button) view;
String btnText = button.getText().toString();
String dataToCalculate = tv_result.getText().toString();
if (btnText.equals("=")){
tv_result.setText(getResult(tv_solution.getText().toString()));
tv_solution.append(dataToCalculate + btnText);
return;
} else if (btnText.equals("CLEAR")) {
tv_solution.setText("");
tv_result.setText("");
return;
} else if (btnText.equals("+") || btnText.equals("-") || btnText.equals("*") || btnText.equals("/")){
tv_solution.setText(tv_result.getText() + btnText);
tv_result.setText("");
}else{
dataToCalculate = dataToCalculate + btnText;
tv_result.setText(dataToCalculate);}
}//當有點擊事件發生時就集中到這裡處理
private String getResult(String dataToCalculate) {
try {
Expression expression = new Expression(dataToCalculate+tv_result.getText().toString());
return String.valueOf(expression.calculate());
}catch (Exception e){
return "Error";
}
}
}
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.demo.R;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GameFunction extends AppCompatActivity {
private EditText et_player_answer;
private Button btn_enter;
private TextView result;
private FloatingActionButton floatingActionButton;
private ArrayList<Integer> CorrectAnswer, PlayerAnswer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_function);
setFindById();
setAnswer();
setListener();
}
private void setListener() {
btn_enter.setOnClickListener(view -> CheckAnswer(et_player_answer.getText().toString()));
floatingActionButton.setOnClickListener(view -> finish());
}
private void CheckAnswer(String player_answer) {
PlayerAnswer = new ArrayList<>();
et_player_answer.setText("");//每次輸入完答案都把EditText清空
String regex = ".*[a-zA-Z].*";//使用正則表達式判斷輸入的答案有沒有英文字母
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(player_answer);
if (player_answer.length() < 4 || matcher.matches()) {
Toast.makeText(this,"請輸入四個數字",Toast.LENGTH_SHORT).show(); return;}
for (int i = 0; i < 4; i++) {
PlayerAnswer.add(Character.getNumericValue(player_answer.charAt(i)));
}//將玩家輸入的答案丟給陣列存取
int quantityA = 0, quantityB = 0;//A跟B的數量
for (int i = 0; i < 4; i++) {
if (CorrectAnswer.contains(PlayerAnswer.get(i))){//先判斷輸入的答案有沒有在正確答案內
if (CorrectAnswer.get(i).equals(PlayerAnswer.get(i))) quantityA++;//如果有就判斷當下的數字是否跟正確答案一樣,是就A+1
else quantityB++;//反之B+1
}
}//判斷幾A幾B
if (quantityA == 4){
Toast.makeText(this,"恭喜~你猜中了!",Toast.LENGTH_SHORT).show();
result.setText("");
setAnswer();
}//當A的數量為四個的時候,就展開新的一輪
result.append("這次輸入 : "+ PlayerAnswer + ",結果為 : " + quantityA + "A" + quantityB + "B\n");
//沒有4A的時候就正常顯示幾A幾B
}
private void setAnswer() {
CorrectAnswer= new ArrayList<>();
ArrayList<Integer> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add(i);
}//先建立一個0到9的陣列
Collections.shuffle(data);//接著將陣列洗牌
CorrectAnswer.addAll(data.subList(0,4));//將前四個數字加進正確解答
Log.d("test", "setAnswer: "+CorrectAnswer);
}//設定正確答案
private void setFindById() {
et_player_answer = findViewById(R.id.et_game);
btn_enter = findViewById(R.id.btn_game);
result = findViewById(R.id.game_result);
floatingActionButton = findViewById(R.id.game_floatActionButton);
}
}