6

There are a list of students.each row shows an Image,Name and number. I created a Room database and only managed to populate "name" and "number" columns into the list using this guide.

When user opens the AddNewStudentActivity, He/She needs to choose a photo from gallery and fill two editTexts for the name and number and click "save" and save to student to the studentDatabase.

the image should be displayed in the list alongside those two texts(name and number).

I have NO IDEA how to do this i only think the process should be like "setting up an intent that opens the gallery and we can choose an image and get it's path stored on the database and display it from database to the list"but don't know how to code it.there are tutorials about this but they were all using SQLITE and not Room and I'm new to the whole database topic.

-Thanks

NewStudentActivity.java

public class NewStudentActivity extends AppCompatActivity {

    public static final String EXTRA_REPLY = "com.example.android.studentlistsql.REPLY";

    private EditText mNameWordView;
    private EditText mNumWordView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_student);
        mNameWordView = findViewById(R.id.name_word);
        mNumWordView = findViewById(R.id.num_word);

        final Button button = findViewById(R.id.button_save);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent replyIntent = new Intent();
                if (TextUtils.isEmpty(mNameWordView.getText())) {
                    setResult(RESULT_CANCELED, replyIntent);
                } else {
                    String word = mNameWordView.getText().toString();
                    replyIntent.putExtra(EXTRA_REPLY, word);
                    setResult(RESULT_OK, replyIntent);
                }
                if (TextUtils.isEmpty(mNumWordView.getText())) {
                    setResult(RESULT_CANCELED, replyIntent);
                } else {
                    String word = mNumWordView.getText().toString();
                    replyIntent.putExtra(EXTRA_REPLY, word);
                    setResult(RESULT_OK, replyIntent);
                }
                finish();
            }
        });

    }
}

StudentListAdapter.java

public class StudentListAdapter extends RecyclerView.Adapter<StudentListAdapter.WordViewHolder> {

    class WordViewHolder extends RecyclerView.ViewHolder {
        private final TextView nameItemView;
        private final TextView numberItemView;

        private WordViewHolder(View itemView) {
            super(itemView);
            nameItemView = itemView.findViewById(R.id.nameTextView);
            numberItemView = itemView.findViewById(R.id.numberTextView);
        }
    }

    private final LayoutInflater mInflater;
    private List<Student> mStudents; // Cached copy of words

    StudentListAdapter(Context context) { mInflater = LayoutInflater.from(context); }

    @Override
    public WordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mInflater.inflate(R.layout.recyclerview_item, parent, false);
        return new WordViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(WordViewHolder holder, int position) {
        if (mStudents != null) {
            Student current = mStudents.get(position);
            holder.nameItemView.setText(current.getStudentName());
            holder.numberItemView.setText(current.getStudentNumber());
        } else {
            // Covers the case of data not being ready yet.
            holder.nameItemView.setText("No Word");
        }
    }

    void setStudents(List<Student> words){
        mStudents = words;
        notifyDataSetChanged();
    }

    // getItemCount() is called many times, and when it is first called,
    // mWords has not been updated (means initially, it's null, and we can't return null).
    @Override
    public int getItemCount() {
        if (mStudents != null)
            return mStudents.size();
        else return 0;
    }
}
Hessam-Emami
  • 347
  • 2
  • 4
  • 15
  • I realized that I answered the question but didn't talk about Room, what exactly was your question about Room, how to insert or query? Or something else. – Suleyman May 30 '18 at 17:26

2 Answers2

5

To be honest the process isn't that much different with Room. As you said, to pick the photo from the Gallery you use an intent:

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "select a picture"), YOUR_IMAGE_CODE);

Then you handle this case in onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == YOUR_IMAGE_CODE) {
        if(resultCode == RESULT_OK)
            Uri selectedImageUri = data.getData();
    }
}

So selectedImageUri is the piece of information that you save in your database. In your Entity class Student.java you can change mStudentPic to String so when you insert your Uri you can use a method:

selectedImageUri.toString();

and when you want to convert it back to Uri:

Uri uri = Uri.parse(yourUriAsString);

I assumed that you know how to insert and query values from the database.

And then in your onBindViewHolder you can use Glide or Picasso, or any other library to load the image, for example with Glide:

Glide.with(context)
.load(new File(uri.getPath()))
.into(imageView);
Suleyman
  • 2,765
  • 2
  • 18
  • 31
  • So `YOUR_IMAGE_CODE` is any number you can define it like that `public static final int YOUR_IMAGE_CODE = 192;`, and `onActivityResult` goes in your Activity. Try that, the Uri should not be null after that. – Suleyman May 30 '18 at 17:56
  • Hi,Thx a lot,I managed to add do the things you said ,now the pictures uri gets added to the database but in the Adapter it still doesnt show the image on the list – Hessam-Emami Jun 01 '18 at 09:25
  • "holder.studImage.setImageURI(Uri.parse(current.getStudentPic()));" it this: "resolveUri failed on bad bitmap uri: content://com.android.providers.media.documents/document/image%3A42 W/ImageView: Unable to open content: content://com.android.providers.downloads.documents/document/raw%3A java.lang.SecurityException: Permission Denial: opening provider com.android.providers.downloads.DownloadStorageProvider from ProcessRecord ... requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs" – Hessam-Emami Jun 01 '18 at 09:26
  • @HessamEmami no problem :) I really think you should try using Picasso or Glide to show the image, it's much easier, instead of `setImageUri`. Judging from the error it seems that you are trying to access files in Downloads folder. Are you using `ACTION_GET_CONTENT`? I'm not sure, but try using `ACTION_OPEN_DOCUMENT` instead, refer to [this](https://stackoverflow.com/questions/36182134/what-is-the-real-difference-between-action-get-content-and-action-open-document) question regarding that matter. – Suleyman Jun 01 '18 at 14:32
  • Thanks!I just rewrote my project and according to your help everything works find and the image loads without using picasso or glide. – Hessam-Emami Jun 02 '18 at 16:35
  • @HessamEmami perfect! Glad I could help :) but think about Glide or Picasso, it provides other advantages as well. Good luck! – Suleyman Jun 02 '18 at 17:07
  • one thing,why does the intent doesn't straightly open 'image gallery' but opens up some other program(documents i think).what changes should be made? – Hessam-Emami Jun 02 '18 at 17:41
  • @HessamEmami Check out [this](https://stackoverflow.com/questions/16928727/open-gallery-app-from-android-intent) post, I believe it should help. – Suleyman Jun 02 '18 at 18:24
  • Will the URI you store in the db always be valid? What if the image gets backed up to the user's Google Photos and deleted from the device? – Randy Apr 02 '19 at 01:23
  • @Randy obviously in that case, the URI won't be valid. You can have a method that compares all the URIs in the database and in the photos directory, and if something doesn't correspond you can delete or add URIs to the database accordingly. – Suleyman Apr 02 '19 at 06:14
  • @Suleyman It's a pretty common use case that user's backup their photos. That makes this method pretty much unusable for apps that want to include photos with user generated content without storing a copy of them. – Randy Apr 04 '19 at 19:14
  • @Randy The OP wasn't interested in that use case, therefore I have not mentioned anything about it, I didn't prepare this solution to be a silver bullet. It's up to you whether you want to store your photos in the database or create a folder and copy the photos there. – Suleyman Apr 04 '19 at 20:41
  • @Suleyman What if the image is deleted from the gallery ? The URI still pointing out to the same file path . which may lead to file not found exception .. – krishnamurthy Apr 10 '19 at 12:20
  • @krishnamurthy as I already said above, you can have a method that compares the URIs in the file directory and the database and deletes them appropriately. And as far as I know Glide will not surface the exception, you will just see the placeholder or the error image. – Suleyman Apr 10 '19 at 15:28
  • What should I pass as a 'context' under the `onBindViewHolder` to `Glide.with(context)`? My signature for `onBindViewHolder` is `public void onBindViewHolder(@NonNull ProductViewHolder holder, int position)` – zodiac645 Apr 28 '20 at 13:24
  • @zodiac645 it's the context of the activity/fragment that is invoking the recycler view, you would normally pass it in a constructor of the recycler view – Suleyman Apr 28 '20 at 14:38
  • I was trying to manage it under the adapter class for the fragment. `onBindViewHolder()` method is in that adapter. – zodiac645 Apr 28 '20 at 14:41
  • [This one](https://stackoverflow.com/questions/61482200/using-glide-library-with-a-fragment-adapter) – zodiac645 Apr 28 '20 at 14:58
  • @zodiac645 you cannot get the context in the parameters of onBindViewHolder, you need to pass it from the activity or fragment, check the first answer to the question that you linked – Suleyman Apr 28 '20 at 18:31
  • But the method itself is under the adapter class, I mean the line `Glide.with(context).load(new File(uri.getPath())).into(holder.productImage);` in the question that I linked (I have just edited it) is under the adapter class. How could I pass the context from the fragment. – zodiac645 Apr 28 '20 at 18:41
  • @zodiac645 sorry for a late reply. Hopefully you found the solution, if you look at any standard implementation of RecyclerView you will notice how the context is passed and will be able to figure out how to use it. Here is a thread which can help https://stackoverflow.com/questions/32136973/how-to-get-a-context-in-a-recycler-view-adapter – Suleyman May 18 '20 at 16:05
2

Also, I am using the same function with SQLite I am the storing just image path and get from SQLite and load image using image URI

holder.imgUserPhoto.setImageURI(Uri.parse(new File(contactModel.getContactPhoto()).toString()));
Milan Pansuriya
  • 2,521
  • 1
  • 19
  • 33