简体   繁体   中英

Starting an intent from custom adapter (not from the main activity) - how to implement on activity result to reload views in adapter?

In my app, I am implementing a custom adapter for a recycler view with multiple views. In my adapter, I case on my list of objects passed in from the main activity and set my views and onClickListeners there. For one of the intents I fire, I am editing something which I set in my adapter. After the activity returns - the adapter does not update. I am not sure how I would implement an interface or onActivityForResult() method for my adapter to reconfigure this view in my RecyclerView after the activity finishes.

I tried implementing an interface but am not sure how to do this exactly as I declare my adapter in my main activity (fragment) and then from there add a list of objects. In my adapter, I set my views there based on which object is added.

Here is the relevant code from my main fragment class: I am adding various objects to mObjects.

public class ProfileFragment extends Fragment{
    public final static String TAG = "ProfileFragment";  // tag for logging from this activity
    final ParseUser user = ParseUser.getCurrentUser();

    // For post feed:
    ArrayList<Object> mObjects;
    ArrayList<Post> mPosts;
    RecyclerView mLayout;
    MultiViewAdapter mMultiAdapter;

    // For stats view:
    HashMap<String, Integer> mCities;  // Contains the cities and number of times visited by user
    HashMap<String, Integer> mCountries;  // Contains the countries and number of times visited by user
    HashMap<String, Integer> mContinents;  // Contains the continents and number of times visited by user


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_profile, container, false);
        setUpLogOutButton(v);

        // Populate stat maps and get posts
        mObjects = new ArrayList<>();
        mCities = new HashMap<>();
        mCountries = new HashMap<>();
        mContinents = new HashMap<>();
        mPosts = new ArrayList<>();

        // Get posts
        getPosts();
        mObjects.add(user);

        // For post feed view:
        mMultiAdapter = new MultiViewAdapter(getActivity(), mObjects);

        mLayout = v.findViewById(R.id.rvPosts);
        mLayout.setLayoutManager(new LinearLayoutManager(getContext()));
        mLayout.setAdapter(mMultiAdapter);

        return v;
    }
}

Here is the relevant code from my adapter:

public class MultiViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    Context mContext;
    ArrayList<Object> items;

    // Identifier for objects in items and which view to load:
    private final int USER_INFO = 0, POST = 1, STAT = 2;

    // The total number of cities, countries, and continents in the world:
    public final int totalNumCities = 4416;
    public final int totalNumCountries = 195;
    public final int totalNumContinents = 7;

    public MultiViewAdapter(Context context, ArrayList<Object> items) {
        this.items = items;
        this.mContext = context;
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    //Returns the view type of the item at position for the purposes of view recycling.
    @Override
    public int getItemViewType(int position) {
        if (items.get(position) instanceof Post) {
            return POST;
        } else if (items.get(position) instanceof ParseUser) {
            return USER_INFO;
        } else if(items.get(position) instanceof HashMap){
            return STAT;
        }
        return -1;
    }


    /**
     * This method creates different RecyclerView.ViewHolder objects based on the item view type.
     *
     * @param viewGroup ViewGroup container for the item
     * @param viewType  type of view to be inflated
     * @return viewHolder to be inflated
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        RecyclerView.ViewHolder viewHolder = null;
        LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
        switch (viewType) {
            case POST:
                View v1 = inflater.inflate(R.layout.item_post_card, viewGroup, false);
                viewHolder = new PostViewHolder(v1);
                break;
            case USER_INFO:
                View v2 = inflater.inflate(R.layout.item_user_information, viewGroup, false);
                viewHolder = new UserInfoViewHolder(v2);
                break;
            case STAT:
                View v3 = inflater.inflate(R.layout.item_user_stats, viewGroup, false);
                viewHolder = new StatViewHolder(v3);
        }
        return viewHolder;
    }

    /**
     * This method internally calls onBindViewHolder(ViewHolder, int) to update the
     * RecyclerView.ViewHolder contents with the item at the given position
     * and also sets up some private fields to be used by RecyclerView.
     *
     * @param viewHolder The type of RecyclerView.ViewHolder to populate
     * @param position   Item position in the viewgroup.
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        switch (viewHolder.getItemViewType()) {
            case POST:
                PostViewHolder vh1 = (PostViewHolder) viewHolder;
                configurePostViewHolder(vh1, position);
                break;
            case USER_INFO:
                UserInfoViewHolder vh2 = (UserInfoViewHolder) viewHolder;
                configureUserInfoViewHolder(vh2, position);
                break;
            case STAT:
                StatViewHolder vh3 = (StatViewHolder) viewHolder;
                configureStatViewHolder(vh3, position);
                break;
        }
    }

    private void configureStatViewHolder(final StatViewHolder vh3, final int position){
        final HashMap<String, Integer> cities = (HashMap<String, Integer>) items.get(position);
        if(cities!= null){
            // For pie chart
            // Cities:
            // TODO: add pie chart for continents and countries and put this in seperate method
            List<PieEntry> pieEntries = new ArrayList<>();
            pieEntries.add(new PieEntry(cities.size(), "Visited Cities"));
            pieEntries.add(new PieEntry(totalNumCities-cities.size(), "Unvisited Cities"));
            PieDataSet pieDataSet = new PieDataSet(pieEntries, "City Stats");
            pieDataSet.setColors(R.color.colorPrimary, R.color.colorPrimaryDark);
            PieData pieData = new PieData(pieDataSet);
            vh3.getmPieChart().setData(pieData);
            vh3.getmPieChart().animateY(1000);
            vh3.getmPieChart().invalidate();
        }
    }

    private void configurePostViewHolder(final PostViewHolder vh1, final int position) {
        final Post post = (Post) items.get(position);
        if (post != null) {
            vh1.getRootView().setTag(post);
            String cityName = post.getCity();
            String countryName = post.getCountry();
            String continentName = post.getContinent();
            vh1.getTvName().setText(cityName + ", " + countryName + ", " + continentName);
            vh1.getTvName().setTextColor(Color.WHITE);
            vh1.getvPalette().setBackgroundColor(ContextCompat.getColor(mContext, R.color.grey));

            SimpleTarget<Bitmap> target = new SimpleTarget<Bitmap>() {
                @Override
                public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                    vh1.getIvProfile().setImageBitmap(resource);
                    Palette p = Palette.from(resource).generate();
                    vh1.getvPalette().setBackgroundColor(ContextCompat.getColor(mContext, R.color.grey));
                }
            };

            vh1.getIvProfile().setTag(target);
            // TODO: Maybe don't crop image as it looks very small
            if(post.getImage()!=null) {
                Glide.with(mContext).asBitmap().load(post.getImage().getUrl()).centerCrop().into(target);
            } else {
                // TODO: fix default image loaded where no image present to be prettier
                Glide.with(mContext).asBitmap().load(R.drawable.ic_add_photo).centerCrop().into(target);
            }
            vh1.getIvProfile().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent it = new Intent(mContext, EditPost.class);
                    Bundle bundle = new Bundle();
                    bundle.putSerializable(Post.class.getSimpleName(), post);
                    it.putExtras(bundle);
                    // Start activity for result to reconfigure user view after return
                    ((Activity) mContext).startActivityForResult(it, 2121);
                }
            });
        }
    }

    private void configureUserInfoViewHolder(UserInfoViewHolder vh2, final int position) {
        ParseUser user = (ParseUser) items.get(position);
        if (user != null) {
            if (user.getParseFile("profileImg") != null) {
                vh2.setmIvProfileImage(user.getParseFile("profileImg").getUrl(), mContext);
            } else {
Glide.with(mContext).load(R.drawable.ic_user).into(vh2.getmIvProfileImage());
            }
            vh2.getmTvEditProfile().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent it = new Intent(mContext, UserSettings.class);
** SEE HERE FOR INTENT FIRED **
                    (mContext).startActivityForResult(it);
                }
            });
        }
    }

From there, I start my UserSettings activity where the user can change there information.

public class UserSettings extends AppCompatActivity {
    ImageView mIvProfileImage;
    TextView mTvEditPhoto;
    ImageView mIvBackArrow;
    ImageView mIvSave;
    EditText mEtUsername;
    EditText mEtNumber;
    EditText mEtEmail;
    public String photoFileName = "photo.jpg";
    public static final int REQUEST_CODE = 101;
    File mPhotoFile;
    ParseFile mParseFile;
    final ParseUser user = ParseUser.getCurrentUser();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_settings);
      //  mIntentListener = getCallingActivity()

        // Set Views
        mIvProfileImage = findViewById(R.id.ivProfileImageMain);
        mTvEditPhoto = findViewById(R.id.tvEditPhoto);
        mIvBackArrow = findViewById(R.id.ivBack);
        mEtUsername = findViewById(R.id.etUsername);
        mEtNumber = findViewById(R.id.etNumber);
        mEtEmail = findViewById(R.id.etEmail);
        mIvSave = findViewById(R.id.ivSave);
        updateCurrentViews();

        // Set on click listener for photo
        mTvEditPhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(UserSettings.this);
                builder.setTitle("Upload or Take a Photo");
                builder.setPositiveButton("Upload", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Upload image
                        startActivityForResult(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI), Constants.GET_FROM_GALLERY);
                    }
                });
                builder.setNegativeButton("Take a Photo", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Take Photo
                        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                        PhotoHelper photoHelper = new PhotoHelper();
                        mPhotoFile = photoHelper.getPhotoFileUri(UserSettings.this, photoFileName);
                        Uri fileProvider = FileProvider.getUriForFile(UserSettings.this, "com.example.fileprovider", mPhotoFile);
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider);
                        if (intent.resolveActivity(UserSettings.this.getPackageManager()) != null) {
                            startActivityForResult(intent, Constants.CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
                        }
                    }
                });
                AlertDialog dialog = builder.create();
                dialog.show();
            }
        });

        // Go back to profile activity if user clicks back
        mIvBackArrow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        mIvSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mParseFile != null) {
                    user.put("profileImg", mParseFile);
                }

                user.setEmail(mEtEmail.getText().toString());
                user.setUsername(mEtUsername.getText().toString());
                user.put("phone", mEtNumber.getText().toString());
                user.put("email", mEtEmail.getText().toString());
                setResult(RESULT_OK, new Intent());
                user.saveInBackground(new SaveCallback() {
                    @Override
                    public void done(ParseException e) {
                        updateCurrentViews();
                        finish();
                    }
                });
            }
        });
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, final Intent data) {
        if (requestCode == Constants.CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            if (resultCode == this.RESULT_OK) {
                Bitmap takenImage = BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath());
                mIvProfileImage.setImageBitmap(takenImage);
                PhotoHelper photoHelper = new PhotoHelper();
                File photoFile = photoHelper.getPhotoFileUri(this, photoFileName);
                mParseFile = new ParseFile(photoFile);
            } else {
                mParseFile = null;
                Toast.makeText(this, "Picture wasn't taken!", Toast.LENGTH_SHORT).show();
            }
        } else {
            if (resultCode == this.RESULT_OK) {
                Bitmap bitmap = null;
                Uri selectedImage = data.getData();
                try {
                    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImage);
                } catch (Exception e) {
                }
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
                byte[] image = stream.toByteArray();
                mParseFile = new ParseFile("profpic.jpg", image);
                final Bitmap finalBitmap = bitmap;
                Glide.with(this).load(finalBitmap).into(mIvProfileImage);
            } else {
                mParseFile = null;
            }
        }
    }

    public void updateCurrentViews() {
        // For profile image:
        mIvProfileImage = findViewById(R.id.ivProfileImageMain);
        if (user.getParseFile("profileImg") != null) {
            Glide.with(this).load(user.getParseFile("profileImg").getUrl()).into(mIvProfileImage);
        } else {
            Glide.with(this).load(R.drawable.ic_user).into(mIvProfileImage);
        }

        // EditText:
        mEtUsername.setText(user.getUsername());
        mEtNumber.setText(String.format("%s", user.get("phone")));
        mEtEmail.setText(String.format("%s", user.get("email")));
    }
}

The issue is when the UserSettingsActivity finishes the updated information is not reflected in the adapter and it shows the old information.

EDIT RESPONSE IN REPLY TO COMMENT Here is my main activity:

public class HomeActivity extends AppCompatActivity {
    final FragmentManager fragmentManager = getSupportFragmentManager();

    final Fragment fragment1 = new MapFragment();
    final Fragment fragment2 = new DiscoverFragment();
    final Fragment fragment3 = new ProfileFragment();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        BottomNavigationView navView = findViewById(R.id.nav_view);
        navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                Fragment fragment;
                switch (menuItem.getItemId()) {
                    case R.id.navigation_home:
                        fragment = fragment1;
                        break;
                    case R.id.navigation_dashboard:
                        fragment = fragment2;
                        break;
                    case R.id.navigation_notifications:
                    default:
                        fragment = fragment3;
                        break;
                }
                fragmentManager.beginTransaction().replace(R.id.flContainer, fragment).commit();
                return true;
            }
        });
        navView.setSelectedItemId(R.id.navigation_home);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

And in my ProfileFragment I have added:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == 2121){
            // the user information is at item 0
            mMultiAdapter.notifyItemChanged(0);
        }
    }

In Adapter I start the activity here with the code:

((Activity) mContext).startActivityForResult(it, 2121);

You need to have onActivityResult() function in your ProfileFragment since it will be called when UserSettings activity is finished. From there you get the intent, and get changed data. Your ProfileFragment has an instance of adapter so you would call notifyItemChanged function after updating the object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM