Issue
I want to display my picture in the gridview. The picture is coming from the path and the path is stored in the SQLite database. I try the tutorial from MSDN here https://docs.microsoft.com/en-us/xamarin/android/user-interface/layouts/grid-view and I modified and changed my resource.drawable.image into the image path (the image path is from the database), but I don't know how to do that. I try using the bitmap method but I still confused about how I can make it array using this method. I want to get the picture data such as photo title, photo description, and photo path from the database and store it into my List<> or array.
I already make the database and try to call it but, I still confused. Please help me.
So, this is an object class for the table. It called Photo.cs
[Table("tblPhoto")]
public class Photo
{
[PrimaryKey, AutoIncrement, Column("pkPhotoID")]
public int PhotoID { get; set; }
[Column("fkUserID")]
public int UserID { get; set; }
public string PhotoPath { get; set; }
public string PhotoName { get; set; }
public string PhotoDescription { get; set; }
private DateTime _creationDate;
public string CreationDate
{
get { return _creationDate.ToString(); }
set { _creationDate = DateTime.ParseExact(value, "yyyy:MM:dd HH:mm:ss", null); }
}
private DateTime _uploadDate;
public string UploadDate
{
get { return _uploadDate.ToString(); }
set { _uploadDate = DateTime.Parse(value); }
}
//show User Photo
public static Photo ShowUserPhoto()
{
return DBManager.Instance.Query<Photo>($"SELECT * FROM tblPhoto a JOIN tblUser b WHERE a.UserID== b.UserID").FirstOrDefault();
}
//show photo path and its photo
public static Photo ShowPhotoPath(string aPhotoPath)
{
return DBManager.Instance.Query<Photo>($"SELECT * FROM tblPhoto WHERE PhotoPath=='{aPhotoPath}'").FirstOrDefault();
}
//show all
public static Photo ShowAllPhoto()
{
return DBManager.Instance.Query<Photo>($"SELECT * FROM tblPhoto").FirstOrDefault();
}
and the second is for ImageAdapter because I want to display the picture in gridview. It called the ImageAdapter.cs
public class ImageAdapter : BaseAdapter
{
private Context context;
private List<string> gridViewString;
private List<string> gridViewImage;
public ImageAdapter(Context context, List<string> gridViewstr, List<string> gridViewImage)
{
this.context = context;
gridViewString = gridViewstr;
this.gridViewImage = gridViewImage;
}
public override int Count
{
get
{
return gridViewString.Count;
}
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view;
LayoutInflater inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
if (convertView == null)
{
view = new View(context);
view = inflater.Inflate(Resource.Layout.gridview_layout, null);
TextView txtview = view.FindViewById<TextView>(Resource.Id.textPhotoTitleViewGrid);
ImageView imgview = view.FindViewById<ImageView>(Resource.Id.imageViewGrid);
txtview.Text = gridViewString[position];
imgview.SetImageBitmap(GetImageBitmapFromDB(gridViewImage[position]));
}
else
{
view = (View)convertView;
}
return view;
}
private Android.Graphics.Bitmap GetImageBitmapFromDB(string aPath)
{
Android.Graphics.Bitmap imageBitmap = null;
var _getPath = Model.Photo.ShowPhotoPath(aPath);
{
var imgPath = _getPath.ShowPhotoPath(aPath);
if (imgPath != null && imgPath.Length > 0)
{
imageBitmap = Android.Graphics.BitmapFactory.DecodeFile(imgPath);
}
}
return imageBitmap;
}
}
and the last is a fragment class called Fragment_home.cs The image should be displayed here
public class Fragment_Home : Android.Support.V4.App.Fragment
{
List<string> m_gridviewstring = new List<string>();
List<string> m_imgview = new List<string>();
GridView m_gridview;
public override void OnCreate(Bundle aSavedInstanceState)
{
base.OnCreate(aSavedInstanceState);
}
public static Fragment_Home NewInstance()
{
var _frag1 = new Fragment_Home { Arguments = new Bundle() };
return _frag1;
}
public override View OnCreateView(LayoutInflater aInflater, ViewGroup aContainer, Bundle aSavedInstanceState)
{
var _ignored = base.OnCreateView(aInflater, aContainer, aSavedInstanceState);
//String stringData = Arguments.GetString("email");
View _view = aInflater.Inflate(Resource.Layout.FragmentHome, null);
//var gridview = _view.FindViewById<GridView>(Resource.Id.gridview);
//gridview.Adapter = new ImageAdapter(Context);
//gridview.ItemClick += Gridview_ItemClick;
//return _view;
var _retrievePic = Model.Photo.ShowAllPhoto();
//_retrievePic.PhotoPath;
ImageAdapter adapter = new ImageAdapter(Activity, m_gridviewstring, m_imgview);
m_gridview = _view.FindViewById<GridView>(Resource.Id.grid_view_image_text);
m_gridview.Adapter = adapter;
return _view;
}
}
Please help me, any help?
Solution
I post two images in /storage/emulated/0/DCIM/Camera/
path and "/storage/emulated/0/Pictures"
path as well. For testing, I re-name Photo names.
Here is running gif(Please ignore the nest fragments, I do not want to create a new demo from start to end,I used my previous demo).
Firstly, I create a PhotoDAO.cs
, it will insert data to DB and read all of data from DB. I have given a PhotoPath and PhotoName when insert data to DB.
public class PhotoDAO
{
static SQLiteConnection db;
public List<Photo> GetAllPhotos()
{
Console.WriteLine("Reading data");
var table = db.Table<Photo>();
List<Photo> photos = table.ToList<Photo>();
return photos;
}
public static void DoSomeDataAccess()
{
Console.WriteLine("Creating database, if it doesn't already exist");
string dbPath = Path.Combine(
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),
"Photo1.db3");
db = new SQLiteConnection(dbPath);
db.CreateTable<Photo>();
if (db.Table<Photo>().Count() == 0)
{
// only insert the data if it doesn't already exist
var newPhoto1 = new Photo();
newPhoto1.UserID = 1;
newPhoto1.PhotoPath = "/storage/emulated/0/Pictures/";
newPhoto1.PhotoName = "icon.png";
newPhoto1.PhotoDescription = "This is a hamburger";
db.Insert(newPhoto1);
var newPhoto2 = new Photo();
newPhoto2.UserID = 2;
newPhoto2.PhotoPath = "/storage/emulated/0/Pictures/";
newPhoto2.PhotoName = "person.jpg";
newPhoto2.PhotoDescription = "This is a person";
db.Insert(newPhoto2);
var newPhoto3 = new Photo();
newPhoto3.UserID = 3;
newPhoto3.PhotoPath = "/storage/emulated/0/DCIM/Camera/";
newPhoto3.PhotoName = "IMG1.jpg";
newPhoto3.PhotoDescription = "This is a IMG1";
db.Insert(newPhoto3);
var newPhoto4 = new Photo();
newPhoto4.UserID = 4;
newPhoto4.PhotoPath = "/storage/emulated/0/DCIM/Camera/";
newPhoto4.PhotoName = "IMG2.jpg";
newPhoto4.PhotoDescription = "This is a IMG2";
db.Insert(newPhoto4);
}
}
}
Then in the Fragment_Gallery.cs
, we set a Adapter
for GridView
. Here is code.
public class Fragment_Gallery : Android.Support.V4.App.Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public static Fragment_Gallery NewInstance()
{
var frag1 = new Fragment_Gallery { Arguments = new Bundle() };
return frag1;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
View view = inflater.Inflate(Resource.Layout.galleryfragment, null);
var gridview = view.FindViewById<GridView>(Resource.Id.gridview);
PhotoDAO.DoSomeDataAccess();
var photoDAO=new PhotoDAO();
List<Photo> photos=photoDAO.GetAllPhotos();
gridview.Adapter = new MyAdapter(this, photos);
return view;
}
}
Here is code about my gridview Adapter. I create a CustomView for test if you need custom the item in gridview in the future. I set Imageview source from local path, please see GetView
method.
internal class MyAdapter :BaseAdapter<Photo>
{
private Fragment_Gallery fragment_Gallery;
private List<Photo> photos;
public MyAdapter(Fragment_Gallery fragment_Gallery, List<Photo> photos)
{
this.fragment_Gallery = fragment_Gallery;
this.photos = photos;
}
public override Photo this[int position] => photos[position];
public override int Count => photos.Count;
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null) // no view to re-use, create new
view = fragment_Gallery.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.custUserID).Text = photos[position].UserID.ToString();
view.FindViewById<TextView>(Resource.Id.custPhotoPath).Text = photos[position].PhotoPath;
view.FindViewById<TextView>(Resource.Id.custPhotoName).Text = photos[position].PhotoName;
view.FindViewById<TextView>(Resource.Id.custPhotoDescription).Text = photos[position].PhotoDescription;
string imgFile = photos[position].PhotoPath + photos[position].PhotoName;
Bitmap myBitmap = BitmapFactory.DecodeFile(imgFile);
view.FindViewById<ImageView>(Resource.Id.custImage).SetImageBitmap(myBitmap);
return view;
}
}
Here is layout about CustomView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="@+id/custImage" />
<TextView
android:layout_width="match_parent"
android:layout_height="20dp"
android:id="@+id/custUserID"
android:text="@string/abc_action_bar_home_description"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="20dp"
android:id="@+id/custPhotoPath"
android:text="@string/abc_action_bar_home_description"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="20dp"
android:id="@+id/custPhotoName"
android:text="@string/abc_action_bar_home_description"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="20dp"
android:id="@+id/custPhotoDescription"
android:text="@string/abc_action_bar_home_description"
/>
</LinearLayout>
In the end, please do not forget to add android.permission.WRITE_EXTERNAL_STORAGE
in your AndroidManifest.xml
Here is my demo, you can download it and make a test(Please add Images like my PersonDAO class or you can change the name of image in PersonDAO class).
https://drive.google.com/file/d/1ipw534Q0C4UxHva3Jiv5SoI0KycifpmI/view?usp=sharing
=====update=======
If you want to achieve the click event, you can use gridview.ItemClick += Gridview_ItemClick;
to achieve it. If you got image position from the adapter is not found, please add a break point Photo photo = photos[e.Position];
in the Gridview_ItemClick
, if you got the e.Position correctly.
public class Fragment_Gallery : Android.Support.V4.App.Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public static Fragment_Gallery NewInstance()
{
var frag1 = new Fragment_Gallery { Arguments = new Bundle() };
return frag1;
}
List<Photo> photos;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
View view = inflater.Inflate(Resource.Layout.galleryfragment, null);
GridView gridview = view.FindViewById<GridView>(Resource.Id.gridview);
gridview.ItemClick += Gridview_ItemClick;
PhotoDAO.DoSomeDataAccess();
var photoDAO=new PhotoDAO();
photos=photoDAO.GetAllPhotos();
gridview.Adapter = new MyAdapter(this, photos);
return view;
}
private void Gridview_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
// throw new NotImplementedException();
Photo photo = photos[e.Position];
Intent intent= new Intent(Context, typeof(DetailActivity));
intent.PutExtra("PicName", photo.PhotoName);
intent.PutExtra("PicDes", photo.PhotoDescription);
StartActivity(intent);
}
}
In the DetailActivity, you can got the picture info by following code.
public class DetailActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Detaillayout);
TextView detailPhotoName = FindViewById<TextView>(Resource.Id.detailPhotoName);
TextView detailPhotoDescription = FindViewById<TextView>(Resource.Id.detailPhotoDescription);
Bundle extras =Intent.Extras;
detailPhotoName.Text = extras.GetString("PicName");
detailPhotoDescription.Text = extras.GetString("PicDes");
// Create your application here
}
}
You want to display the picture bigger in the fragment layout or the DetailActivity
? In the fragment layout, just set bigger value for columnWidth=200dp
in GridView
and Open the CustomView.xml
, set bigger value for android:layout_width="200dp" android:layout_height="200dp"
in ImageView
Here is click running gif.
Answered By - Leon Lu - MSFT
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.