Щелкнув элемент RecyclerView, startActivityForResult отправляет данные, но возвращенное намерение не содержит данных

#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 может быть нулевым, но данные все еще передавались. Возможно, данные хранятся в другом месте Намерения…