非法状态异常:无法执行 android:onClick 的方法,尝试迁移到另一个页面?



由于大学实习要求,我一直在开发一个应用程序(我在讲师的推荐下从另一个学生的最后一年项目中接手了该应用程序(,并且在从主菜单屏幕迁移到登录屏幕时遇到问题。

(只有登录页面有问题,从应用程序的主菜单转到注册页面没有任何问题,因此我认为问题可能出在 xml 文件或 LoginActivity.java 中,但不知道如何。

日志猫错误如下列出:

TAL EXCEPTION: main
Process: com.finchvpn.androidcloudpark, PID: 1579
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:389)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:384)
at android.view.View.performClick(View.java:4438) 
at android.view.View$PerformClick.run(View.java:18422) 
at android.os.Handler.handleCallback(Handler.java:733) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:136) 
at android.app.ActivityThread.main(ActivityThread.java:5001) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 
at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.NullPointerException
at com.finchvpn.androidcloudpark.MainActivity.loginButtonClick(MainActivity.java:74)

以下是相关的 xml 文件:

主菜单 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"
android:background="@drawable/asd2">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar3"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/side_nav_bar"
app:popupTheme="@style/PopupOverlay" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="239dp"
android:layout_height="73dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:text="CloudPark.my"
android:textColor="@color/md_white_1000"
android:textSize="14pt"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.11"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.014" />
<ImageButton
android:id="@+id/loginButtonPic"
android:layout_width="132dp"
android:layout_height="120dp"
android:layout_gravity="end"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:background="@null"
android:onClick="loginButtonClick"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.19"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.559"
app:srcCompat="@drawable/loginnew2"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/RegisterButtonPic"    
android:layout_width="132dp"
android:layout_height="120dp"
android:layout_gravity="end"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:background="@null"
android:onClick="registerButtonClick"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.813"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.559"
app:srcCompat="@drawable/registernew1"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/textView2"
android:layout_width="279dp"
android:layout_height="63dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="CloudPark, the next generation parking app."
android:textColor="@color/md_white_1000"
android:textSize="10pt"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.505"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.181" />
</android.support.constraint.ConstraintLayout>

登录 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"
android:background="@drawable/asd2">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar3"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/side_nav_bar"
app:popupTheme="@style/PopupOverlay" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="307dp"
android:layout_height="91dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:text="Login"
android:textColor="@color/md_white_1000"
android:textSize="14pt"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.506"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/textView2"
android:layout_width="281dp"
android:layout_height="50dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Enter your credentials to access CloudPark."
android:textColor="@color/md_white_1000"
android:textSize="10pt"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.505"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.181" />
<EditText
android:id="@+id/textUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="Username"
android:inputType="textEmailAddress"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.503"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.352" />
<EditText
android:id="@+id/textPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:ems="10"
android:hint="Password"
android:inputType="textPassword"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.503"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.443" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="#0026FF"
android:onClick="loginButtonClick"
android:text="Login to CloudPark"
android:textColor="#FFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.687" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="20dp" />
</android.support.constraint.ConstraintLayout>

登录活动.java

public class LoginActivity extends AppCompatActivity {
private EditText textUsername;
private EditText txtPassword;
private static RestClient restClient = new RestClient();
private SharedPreferences.Editor sharedPreferencesEditor;
@SuppressLint("CommitPrefEdits")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
try {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
} catch (Exception e) {
}
textUsername = findViewById(R.id.textUsername);
txtPassword = findViewById(R.id.textPassword);
SharedPreferences sharedPreferences = getSharedPreferences("UserInfo", 0);
sharedPreferencesEditor = sharedPreferences.edit();
textUsername.setText(sharedPreferences.getString("textUsername", ""));
txtPassword.setText(sharedPreferences.getString("txtPassword", ""));
}
public static RestClient getRestClient() {
return restClient;
}
public void loginButtonClick(View v) {
if (!textUsername.getText().toString().equals("") && !txtPassword.getText().toString().equals("")) {
apiPostLogin(Constants.ANDROID_KEY + ":" + textUsername.getText().toString() + ":" + txtPassword.getText().toString());
sharedPreferencesEditor.putString("textUsername", textUsername.getText().toString());
sharedPreferencesEditor.putString("txtPassword", txtPassword.getText().toString());
sharedPreferencesEditor.commit();
} else {
Toast.makeText(LoginActivity.this, "NULL", Toast.LENGTH_LONG).show();
}
}
private void apiPostLogin(String data) {
final ProgressDialog progress = new ProgressDialog(this);
progress.setTitle("Logging in");
progress.setMessage("Please wait ...");
progress.setCancelable(false); // disable dismiss by tapping outside of the dialog
progress.show();
Call<ResponseBody> call = getRestClient().getLoginService().postLogin(data);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful() && response.body() != null) {
try {
String data = response.body().string();
JSONObject jsonObject = new JSONObject(data);
Constants.uid = Integer.parseInt(jsonObject.getString("id"));
Constants.username = jsonObject.getString("username");
Constants.email = jsonObject.getString("email");
Constants.credit = jsonObject.getString("credit");
Constants.qr_code = jsonObject.getString("qr_code");
Constants.created_at = jsonObject.getString("created_at");
Constants.updated_at = jsonObject.getString("updated_at");
Toast.makeText(LoginActivity.this, "apiPostLogin onResponse <<<< rnrn" + jsonObject.toString(), Toast.LENGTH_LONG).show();
Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
progress.dismiss();
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(LoginActivity.this, "Incorrect username/password, please try again." + t.getMessage(), Toast.LENGTH_LONG).show();
progress.dismiss();
}
});
}
}

我怀疑这个问题与主菜单xml和登录xml中的android:onClick="loginButtonClick"冲突有关。

您不应依赖android:onClick属性来处理视图点击。这是因为在使用android:onClick时,您无法确定处理onClick的方法是否有效。没有确切的机制来确保代码连接到视图。另一个问题是,android:onClick不适用于Fragment。所以,我认为使用android:onClick是不好的做法。

要解决此问题,请在视图上使用setOnClickListener。再加上findViewById,您的代码将更加健壮,因为如果您为findViewById提供了不正确的 id,您将始终看到错误。若要使代码更可靠并避免与 id 冲突,需要为视图使用描述性名称 id。使用如下命名约定:

layout name+_+What the view for+_+type of view

例如,对于您的登录 xml,您可以使用如下内容:

....
<Button
android:id="@+id/login_cloudpark_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="#0026FF"
android:text="Login to CloudPark"
android:textColor="#FFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.687" />
<android.support.constraint.Guideline
android:id="@+id/login_begin_gdl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="20dp" />
...

然后与findViewById一起使用:

public class LoginActivity extends AppCompatActivity {
...
private Button mBtnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
...
mBtnLogin = findViewById(R.id.login_cloudpark_btn);
}
}

之后,将点击监听器添加到mBtnLogin

mbtnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// place your clicking handle code here.
}
});

通过执行上述操作,您将完全分离 xml 中的视图和逻辑。