#java #android-intent #android-recyclerview #startactivityforresult
Вопрос:
У меня возникли проблемы с получением данных обратно с помощью startActivityForResult из элемента RecyclerView. Все отлично работает при отправке намерения для нового действия, но когда возвращаемые данные должны быть пойманы в обратном вызове onActivityResult, намерение не содержит данных.
Я добавил эти комментарии в код, если это поможет:
/********** ONE: Send user1 to TestForResultActivity ************/
/********** TWO: Get user1 from TestActivity ************/
/********** THREE: Send user2 back to TestActivity ************/
/********** FOUR: Get user2 back from TestForResultActivity ************/
TestActivity.java: Успешно отправляет данные, содержащиеся в намерении, в TestForResultActivity. Ошибка при попытке перехватить данные, возвращенные из TestForResultActivity. (См. Комментарии в коде)
public class TestActivity extends AppCompatActivity implements TestAdapter.ITestAdapterListener {
TextView testTextView;
ActivityResultLauncher<Intent> testLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// Initialize the RecyclerView
RecyclerView testRecyclerView;
RecyclerView.LayoutManager testLayoutManager;
TestAdapter testAdapter = new TestAdapter(this);
testRecyclerView = (RecyclerView)findViewById(R.id.testRecyclerView);
testRecyclerView.setHasFixedSize(true);
testLayoutManager = new LinearLayoutManager(this);
testRecyclerView.setLayoutManager(testLayoutManager);
testRecyclerView.setAdapter(testAdapter);
List<User> users = new ArrayList<>();
// Add one user to the list
users.add(new User("user1"));
testAdapter.setUserList(users);
// Define the ActivityForResult
ActivityResultContracts.StartActivityForResult selectMatchingRouteStartActivityForResult = new ActivityResultContracts.StartActivityForResult();
ActivityResultCallback<ActivityResult> selectMatchingRouteActivityResultCallback = new ActivityResultCallback<ActivityResult>() {
// Run this code when the ActivityForResult is finished.
@Override
public void onActivityResult(ActivityResult activityResult) {
if (activityResult.getResultCode() == Activity.RESULT_OK) {
Intent intent2 = activityResult.getData(); /********** FOUR: Get user2 back from TestForResultActivity ************/
Bundle bundle = intent2.getExtras();
User user2 = (User)bundle.getSerializable("USER_KEY");
testTextView.setText(user2.getUsername()); // <--NullPointerException (never got user2 from TestForResultActivity)
} else {
Toast.makeText(TestActivity.this, "Error", Toast.LENGTH_SHORT).show();
}
}
};
// Launch the ActivityForResult
testLauncher = registerForActivityResult(selectMatchingRouteStartActivityForResult, selectMatchingRouteActivityResultCallback);
}
// When the user clicks an item in the RecyclerView, this code is executed.
@Override
public void userItemClicked(User user) {
Intent intent1 = new Intent(this, TestForResultActivity.class);
Bundle bundle1 = new Bundle();
bundle1.putSerializable("USER_KEY", user);
intent1.putExtras(bundle1);
testLauncher.launch(intent1); /********** ONE: Send user1 to TestForResultActivity ************/
}
}
TestForResultActivity.java:
Успешно улавливает данные из тестовой активности
В момент finish()
выполнения intent2 ДЕЙСТВИТЕЛЬНО содержит пользовательские данные.
public class TestForResultActivity extends AppCompatActivity {
TextView testResultTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_for_result);
Button testResultButton = findViewById(R.id.testResultButton);
testResultTextView = findViewById(R.id.testResultTextView);
// Retrieve and display the data from TestActivity
Intent intentFromTestActivity = getIntent(); /********** TWO: Get user1 from TestActivity ************/
Bundle bundle = intentFromTestActivity.getExtras();
User user = (User)bundle.getSerializable("USER_KEY");
testResultTextView.setText(user.getUsername());
// When the user clicks the button, send data back to TestActivity
testResultButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
User user2 = new User("user2");
Bundle test2Bundle = new Bundle();
test2Bundle.putSerializable("USER_KEY", user2);
Intent intent2 = getIntent();
intent2.putExtras(test2Bundle);
setResult(RESULT_OK, intent2);
finish(); /********** THREE: Send user2 back to TestActivity ************/
}
});
}
}
TestAdapter.java:
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.TestViewHolder> {
private List<User> userList = new ArrayList<>();
private TestAdapter.ITestAdapterListener mListener;
public TestAdapter(TestAdapter.ITestAdapterListener mListener) {
this.mListener = mListener;
}
// Link the TestViewHolder to the item's xml file (user_row.xml)
@Override
public TestAdapter.TestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layout = (View) LayoutInflater.from(parent.getContext())
.inflate(R.layout.user_row,parent,false);
TestAdapter.TestViewHolder trackViewHolder = new TestAdapter.TestViewHolder(layout, mListener);
//Create the view holder using the layout from user_row.
return trackViewHolder;
}
// When the user_row.xml layout is bound to the TestViewHolder, massage the data of the current
// position's item. (If there are 5 items in the recycler view, position defines which one to massage.
@Override
public void onBindViewHolder(@NonNull TestAdapter.TestViewHolder holder, int position) {
User currentUser = userList.get(position);
holder.usernameTextView.setText(currentUser.getUsername());
holder.position = position;
holder.user = currentUser;
}
@Override
public long getItemId(int position) {
return super.getItemId(position);
}
@Override
public int getItemCount() {
return userList.size();
}
public void setUserList(List<User> users) {
this.userList = users;
notifyDataSetChanged();
}
public static class TestViewHolder extends RecyclerView.ViewHolder {
private TextView usernameTextView;
private ConstraintLayout userRowLayout;
private User user;
int position;
public TestViewHolder(@NonNull final View v, final TestAdapter.ITestAdapterListener mListener) {
super(v);
position = getAbsoluteAdapterPosition();
userRowLayout = v.findViewById(R.id.layout);
usernameTextView = v.findViewById(R.id.usernameTextView);
userRowLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mListener.userItemClicked(user);
}
});
}
}
// This interface is required to be over-ridden in TestActivity
// It tells the recycler view which user was clicked
public interface ITestAdapterListener{
void userItemClicked(User user);
}
}
User.java:
(Упрощено для использования в этом примере)
public class User implements Serializable {
private String username;
public User(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Ответ №1:
Упс, забыл инициализировать testTextView, что привело к исключению нулевого указателя.
Я просто добавил testTextView = findViewById(R.id.testTextView);
, и все отлично работает.
Данные передавались правильно, но отладчик вводил меня в заблуждение. Я отслеживал переменные намерения, которые иногда показывали mExtras.mMap как null, хотя я ожидал, что переданная переменная пакета будет находиться там.
Очевидно, mMap может быть нулевым, но данные все еще передавались. Возможно, данные хранятся в другом месте Намерения…