chore: initialize repository with gitignore
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
configurations.configureEach {
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.example.task2complexcontrols'
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.example.task2complexcontrols'
|
||||
minSdk 26
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName '1.0'
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
testOptions {
|
||||
animationsDisabled true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.core:core:1.13.1'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.1'
|
||||
androidTestImplementation 'junit:junit:4.13.2'
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.example.task2complexcontrols;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
|
||||
import static androidx.test.espresso.action.ViewActions.replaceText;
|
||||
import static androidx.test.espresso.action.ViewActions.scrollTo;
|
||||
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withSpinnerText;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.app.UiAutomation;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MainActivityComplexUiTest {
|
||||
|
||||
private ActivityScenario<MainActivity> activityScenario;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
activityScenario = ActivityScenario.launch(MainActivity.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
activityScenario.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 协议复选框应直接控制提交按钮是否可点击。
|
||||
*/
|
||||
@Test
|
||||
public void testAgreementToggle_ChangesButtonState() {
|
||||
onView(withId(R.id.btn_submit)).check(matches(not(isEnabled())));
|
||||
|
||||
onView(withId(R.id.cb_agreement)).perform(scrollTo(), click());
|
||||
|
||||
onView(withId(R.id.cb_agreement)).check(matches(isChecked()));
|
||||
onView(withId(R.id.btn_submit)).check(matches(isEnabled()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单选按钮、复选框和 Spinner 都应支持正确选择并反映当前状态。
|
||||
*/
|
||||
@Test
|
||||
public void testSelectionControls_UpdateUiStateCorrectly() {
|
||||
onView(withId(R.id.et_name)).perform(replaceText("赵六"), closeSoftKeyboard());
|
||||
onView(withId(R.id.rb_female)).perform(click());
|
||||
onView(withId(R.id.cb_sport)).perform(scrollTo(), click());
|
||||
onView(withId(R.id.cb_read)).perform(scrollTo(), click());
|
||||
selectCity("上海");
|
||||
|
||||
onView(withId(R.id.et_name)).check(matches(withText("赵六")));
|
||||
onView(withId(R.id.rb_female)).check(matches(isChecked()));
|
||||
onView(withId(R.id.cb_sport)).check(matches(isChecked()));
|
||||
onView(withId(R.id.cb_read)).check(matches(isChecked()));
|
||||
onView(withId(R.id.spinner_city)).check(matches(withSpinnerText(containsString("上海"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整提交流程应输出与 Spinner 当前选择一致的 Toast 提示。
|
||||
*/
|
||||
@Test
|
||||
public void testSubmitFlow_ShowsToastWithSelectedCity() {
|
||||
onView(withId(R.id.et_name)).perform(replaceText("孙七"), closeSoftKeyboard());
|
||||
onView(withId(R.id.rb_male)).perform(click());
|
||||
onView(withId(R.id.cb_sport)).perform(scrollTo(), click());
|
||||
onView(withId(R.id.cb_music)).perform(scrollTo(), click());
|
||||
selectCity("深圳");
|
||||
onView(withId(R.id.cb_agreement)).perform(scrollTo(), click());
|
||||
|
||||
onView(withId(R.id.rb_male)).check(matches(isChecked()));
|
||||
onView(withId(R.id.cb_sport)).check(matches(isChecked()));
|
||||
onView(withId(R.id.cb_music)).check(matches(isChecked()));
|
||||
onView(withId(R.id.spinner_city)).check(matches(withSpinnerText(containsString("深圳"))));
|
||||
onView(withId(R.id.btn_submit)).check(matches(isEnabled()));
|
||||
|
||||
clickAndAssertToast("提交成功!您选择的城市是:深圳");
|
||||
}
|
||||
|
||||
private void selectCity(String city) {
|
||||
onView(withId(R.id.spinner_city)).perform(scrollTo(), click());
|
||||
onData(allOf(is(instanceOf(String.class)), is(city))).perform(click());
|
||||
}
|
||||
|
||||
private void clickAndAssertToast(String message) {
|
||||
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
|
||||
|
||||
try {
|
||||
AccessibilityEvent event = uiAutomation.executeAndWaitForEvent(
|
||||
() -> onView(withId(R.id.btn_submit)).perform(scrollTo(), click()),
|
||||
candidate -> candidate.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED
|
||||
&& containsText(candidate, message),
|
||||
5000
|
||||
);
|
||||
assertNotNull(event);
|
||||
} catch (TimeoutException exception) {
|
||||
fail("Toast not shown: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsText(AccessibilityEvent event, String message) {
|
||||
for (CharSequence text : event.getText()) {
|
||||
if (message.contentEquals(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Task2ComplexControls">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.example.task2complexcontrols;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private EditText etName;
|
||||
private Spinner spinnerCity;
|
||||
private CheckBox cbAgreement;
|
||||
private Button btnSubmit;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
etName = findViewById(R.id.et_name);
|
||||
spinnerCity = findViewById(R.id.spinner_city);
|
||||
cbAgreement = findViewById(R.id.cb_agreement);
|
||||
btnSubmit = findViewById(R.id.btn_submit);
|
||||
|
||||
setupCitySpinner();
|
||||
setupAgreementRule();
|
||||
setupSubmitAction();
|
||||
}
|
||||
|
||||
private void setupCitySpinner() {
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
this,
|
||||
R.array.city_options,
|
||||
android.R.layout.simple_spinner_item
|
||||
);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinnerCity.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupAgreementRule() {
|
||||
// 未勾选协议时禁止提交,勾选后恢复可点击。
|
||||
btnSubmit.setEnabled(false);
|
||||
cbAgreement.setOnCheckedChangeListener((buttonView, isChecked) -> btnSubmit.setEnabled(isChecked));
|
||||
}
|
||||
|
||||
private void setupSubmitAction() {
|
||||
btnSubmit.setOnClickListener(view -> {
|
||||
String city = String.valueOf(spinnerCity.getSelectedItem());
|
||||
String message = getString(R.string.toast_submit_message, city);
|
||||
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/title_form"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:hint="@string/hint_name"
|
||||
android:inputType="textPersonName" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_gender"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_gender"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_male"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/option_male" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_female"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:text="@string/option_female" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_hobbies"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_sport"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/option_sport" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/option_read" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_music"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/option_music" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_city"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_city"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_agreement"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/label_agreement" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_submit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:text="@string/action_submit" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="city_options">
|
||||
<item>北京</item>
|
||||
<item>上海</item>
|
||||
<item>广州</item>
|
||||
<item>深圳</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Task 2 Complex Controls</string>
|
||||
<string name="title_form">用户信息提交</string>
|
||||
<string name="hint_name">请输入姓名</string>
|
||||
<string name="label_gender">请选择性别</string>
|
||||
<string name="label_hobbies">请选择兴趣爱好</string>
|
||||
<string name="label_city">请选择城市</string>
|
||||
<string name="label_agreement">我已阅读并同意用户协议</string>
|
||||
<string name="action_submit">提交信息</string>
|
||||
<string name="option_male">男</string>
|
||||
<string name="option_female">女</string>
|
||||
<string name="option_sport">运动</string>
|
||||
<string name="option_read">阅读</string>
|
||||
<string name="option_music">音乐</string>
|
||||
<string name="toast_submit_message">提交成功!您选择的城市是:%1$s</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Task2ComplexControls" parent="Theme.AppCompat.Light.NoActionBar" />
|
||||
</resources>
|
||||
Reference in New Issue
Block a user