I have a listview that loads information from sqlite database. The information should load image this way:
This is the code for the listview activity:
private void populateListViewFromDB() {
Cursor cursor = myDb.getAllRows();
// Allow activity to manage lifetime of the cursor.
// DEPRECATED! Runs on the UI thread, OK for small/short queries.
startManagingCursor(cursor);
// Setup mapping from cursor to view fields:
String[] fromFieldNames = new String[]
{DBAdapter.KEY_DATE, DBAdapter.KEY_IMG, DBAdapter.KEY_FAVCOLOUR};
int[] toViewIDs = new int[]
{R.id.item_date, R.id.item_icon, R.id.item_kcal};
// Create adapter to may columns of the DB onto elemesnt in the UI.
SimpleCursorAdapter myCursorAdapter =
new SimpleCursorAdapter(
this, // Context
R.layout.item_layout, // Row layout template
cursor, // cursor (set of DB records to map)
fromFieldNames, // DB Column names
toViewIDs // View IDs to put information in
);
// Set the adapter for the list view
ListView myList = (ListView) findViewById(R.id.listViewFromDB);
myList.setAdapter(myCursorAdapter);
}
Theoretically, I'm trying to save the string "snapPath" to the field "KEY_IMG" and load the image into imageview "item_icon". If the user does not snap a photo, by default, the imageview will load a drawable instead.
At the Add activity page, I added a string and save that string to the database:
String snapPath = "res/drawable-xxhdpi/ic_launcher.png"; //by default it will load a drawable
myDb.insertRow(date, snapPath, kcal+" kcal"); //saves into database
Also in Add activity page, the code for capturing and saving the image into my phone:
private void doTakePicture() {
// create a File object for the parent directory
File newDir = new File( Environment.getExternalStorageDirectory(), "/myFoodDiary/snaps");
newDir.mkdirs();
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
//for file name
Date cDate = new Date();
final String fDate = new SimpleDateFormat("yyyyMMMd").format(cDate);
final String fTime = new SimpleDateFormat("HHmmss").format(cDate);
String snapName = "mFD-" + fDate + fTime + ".jpg";
fileJpeg = new File(newDir, snapName);
snapPath = "/myFoodDiary/snaps/"+String.valueOf(snapName);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(fileJpeg));
startActivityForResult(takePictureIntent, TAKE_PIC_REQ);
}
Thanks in advance!
To customize whether to draw a bitmap from storage or a drawable from your resources, you can't use SimpleCursorAdapter anymore. You need to derive a new class from CursorAdapter and use that.
Conditionally loading the images is not as trivial as it seems. I recommend using the Android Query library image loading methods which support placeholders, fallbacks, and asynchronous loading - just what you need.
Related
I am trying to implement a feature like Instagram or WhatsApp, where the thumbnail of a single image that exists in a folder in android, is shown on top of a list item, more like a sample of what kinds of image are in the folder.
Help me to understand this feature.
How I implemented it. It might not be the best though, but it works.
I fetched the URIs of all the images using MediaStore, you can learn how to use it here.
The First step was done in a background thread to prevent it from blocking the UI thread.
I sorted out the images I got, grouping them in a List<Image>, which would represent a single directory.
I then added the List<Image> into a List<List<Image>>, which served as the overall images that were fetched and have their total size which I used later to track the number of images in the directory.
The code is below.
#Override
public void run() {
Uri storageUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
storageUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
storageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
// the queries to the MediaStore API (The image details or metadata I need
String[] projection = {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DISPLAY_NAME};
// now query the MediaStore API using ContentResolver
Cursor imgCursor = getApplicationContext().getContentResolver().query(storageUri, projection, null, null, null);
int bucketId = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int imgSize = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE);
int name = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
int bucketName = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
// directoryDictionary is a temporary list of directory names that was found, while querying the MediaStore API
List<String> directoryDictionary = new ArrayList<>();
// generalList is just a list that would represent a general image list, where all images can be found. Just like Whatsapp
List<Image> generalList = new ArrayList<>();
while (imgCursor.moveToNext()) {
long id = imgCursor.getLong(bucketId);
int size = imgCursor.getInt(imgSize);
String fileName = imgCursor.getString(name);
String folderName = imgCursor.getString(bucketName);
// As recommended by the Android developers doc
Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
// a single image
Image currentImage = new Image(contentUri, size, fileName, folderName);
// add all images to the general image list, but modifying the directory name
Image genImage = new Image(contentUri, size, fileName, "All Media");
generalList.add(genImage);
int directoryIndex = CollectionUtils.linearSearch(directoryDictionary, folderName);
// if search result (directoryIndex) passes this test, then it means that there is
// no such directory in list of directory names
if (directoryIndex < 0) {
imageDirectoryList.add(new ArrayList<>());
directoryDictionary.add(folderName);
directoryIndex = CollectionUtils.linearSearch(directoryDictionary, folderName);
if (directoryIndex >= 0)
imageDirectoryList.get(directoryIndex).add(currentImage);
} else {
imageDirectoryList.get(directoryIndex).add(currentImage);
}
}
//...then add it if the image list of folder is > 2
if (imageDirectoryList.size() > 2) imageDirectoryList.add(0, generalList);
imgCursor.close();
runOnUiThread(() -> {
// imageAdapter is the RecyclerView's list Adapter.
// notifyDataSetChanged() must be call to refresh list.
imageAdapter.notifyDataSetChanged();
// doViewUpdate was just used to turn on and off the visibility of some views
doViewUpdate();
});
}
I am trying to store and retrieve image data in Sqlite Db.
To do so I firstly stored in local device memory an example pic (path: storage/emulated/0/Download/).
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private final String SAMPLE_IMAGE_PATH = "/storage/emulated/0/Download/image.jpg";
Then I set up an insert method to feed the db with these example data:
private void insertProduct() {
// Create a ContentValues object where column names are the keys,
// and sample attributes are the values.
ContentValues values = new ContentValues();
values.put(InventoryContract.ProductEntry.COLUMN_PRODUCT_NAME, sampleName);
values.put(InventoryContract.ProductEntry.COLUMN_PRODUCT_QTY, sampleQty);
values.put(InventoryContract.ProductEntry.COLUMN_PRODUCT_PRICE, SamplePrice);
values.put(InventoryContract.ProductEntry.COLUMN_EMAIL, sampleMail);
values.put(InventoryContract.ProductEntry.COLUMN_PHONE, samplePhone);
values.put(InventoryContract.ProductEntry.COLUMN_PRODUCT_PIC, SAMPLE_IMAGE_PATH);
//insert a new row
Uri newUri = getContentResolver().insert(InventoryContract.ProductEntry.CONTENT_URI,values);
}
and I define the onCreateLoader method as follows:
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Define a projection that specifies the columns from the table we care about.
String[] projection = {
InventoryContract.ProductEntry._ID,
InventoryContract.ProductEntry.COLUMN_PRODUCT_PIC,
InventoryContract.ProductEntry.COLUMN_PRODUCT_PRICE,
InventoryContract.ProductEntry.COLUMN_PRODUCT_QTY,
InventoryContract.ProductEntry.COLUMN_PRODUCT_NAME};
// This loader will execute the ContentProvider's query method on a background thread
return new CursorLoader(this,
InventoryContract.ProductEntry.CONTENT_URI,
projection,
null,
null,
null);
}
In the CursorAdapter class I updated the listView adding the data from db in bindView() method:
public void bindView(View view, Context context, Cursor cursor) {
// Find individual views that we want to modify in the list item layout
TextView nameTextView = (TextView) view.findViewById(R.id.prod_name);
TextView priceTextView = (TextView) view.findViewById(R.id.prod_price);
TextView qtyTextView = (TextView) view.findViewById(R.id.prod_qty);
ImageView prodImageView = (ImageView) view.findViewById(R.id.prod_img);
// Find the columns of attributes that we're interested in
int nameColumnIndex = cursor.getColumnIndex(InventoryContract.ProductEntry.COLUMN_PRODUCT_NAME);
int priceColumnIndex = cursor.getColumnIndex(InventoryContract.ProductEntry.COLUMN_PRODUCT_PRICE);
int qtyColumnIndex = cursor.getColumnIndex(InventoryContract.ProductEntry.COLUMN_PRODUCT_QTY);
int picColumnIndex = cursor.getColumnIndex(InventoryContract.ProductEntry.COLUMN_PRODUCT_PIC);
// Read the attributes from the Cursor for the current product
String prodName = cursor.getString(nameColumnIndex);
Double prodPrice = cursor.getDouble(priceColumnIndex);
int prodQty = cursor.getInt(qtyColumnIndex);
byte [] prodImg = cursor.getBlob(picColumnIndex);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[1024 * 32];
Bitmap bmp = BitmapFactory.decodeByteArray(prodImg, 0, prodImg.length, options);
//Update Views
nameTextView.setText(String.valueOf(prodName));
priceTextView.setText(prodPrice.toString());
qtyTextView.setText(String.valueOf(prodQty));
prodImageView.setImageBitmap(bmp);
}
}
When I try execute this code everything goes ok, but I see a blank image instead of both the selected pic and placer pic.
So I think that there is some problem with inserting data into db.
I am trying to store and retrieve image data in Sqlite Db
I do not recommend this. Store the images in files. Store data in the rows that identifies the files.
Then I set up an insert method to feed the db with these example data
You are storing a string in COLUMN_PRODUCT_PIC. You are not storing a byte[]. This is good, relative to my recommendation. This is bad relative to your data-retrieval code, where you are attempting to retrieve a byte[].
It shows database first data, but i want to show all data which number is 3 column table of each row, and after it will open after click a button and open this list in a new activity
//Read Database
public void readDB(View v) {
SQLiteDatabase db2 = openOrCreateDatabase(" Result ", MODE_PRIVATE, null);
String strThree = "SELECT * FROM my_result";
Cursor c = db2.rawQuery(strThree, null);
c.moveToNext();
String grade = c.getString(c.getColumnIndex("Grade_Point"));
String ss = c.getString(c.getColumnIndex("Subject_Name"));
Toast.makeText(getApplicationContext(), " Subject Name is "+ss+" and Gragde point is"+grade , Toast.LENGTH_LONG).show();
}
Store the extracted values from your column 3 into a variable preferably in an arraylist. Then On the click of your button send it to your activity and populate your listview in that activity in the onCreate() method of that activity. Example below(Not tested)
ArrayList<String> col_3 = new ArrayList<String>();
void readDB(View v){
SQLiteDatabase db2 = openOrCreateDatabase(" Result ", MODE_PRIVATE, null);
String strThree = "SELECT * FROM my_result";
Cursor c = db2.rawQuery(strThree, null);
while(c.moveToNext()!=null){
col_3.add(c.getString(2)) //since 3rd column
}
}
Now on the onClick of your button send it to the destination activity via intents
Intent intent = new Intent(CurrentActivity.this, DestinationActivity.class);
intent.putStringArrayListExtra("col_3_data", col_3);
startActivity(intent);
onCreate() method of your destination Activity will be something like this
Intent i = new Intent();
ArrayList col_value =new ArrayList<String>();
col_value = i.getStringArrayListExtra("col_3_data");
ListView lv = (ListView)findViewById(R.id.my_lsitview); //my_listview is your listview where you want to display your data
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, col_value );
lv.setAdapter(arrayAdapter);
I suggest instead of creating a new activity, you can create a dialog with listview, follow one of the following tutorials:
http://envyandroid.com/creating-listdialog-with-images-and-text/
http://www.edumobile.org/android/custom-listview-in-a-dialog-in-android/
i have a listview with data from database using an sqlite.
when first time (onCreate()) i show the data, there`s no problems, the data all shown.
the problem is, when i want to filter my list view using datepicker and button, the listview not change the data.
ive use listview.invalidateViews(), notifyDataSetChanged()... but still not solved yet. maybe theres some mistake with my code... hope you guys can give me some solution.
thx...
here`s my code
Getting Data From DataBase
public ArrayList<BonRokokModel> getBonRokokList(String userGUID, String transDate, String searchText){
dbPath = "/data/data/app.chameleon.mobile/databases/";
dbName = "SalesTrans.sqlite";
bonRokok = SQLiteDatabase.openDatabase(dbPath + dbName, null,SQLiteDatabase.OPEN_READWRITE);
String strQuery;
strQuery = "SELECT BonRokokID, TransDate, Gudang, Status FROM tblSATBonRokok WHERE UserGUID = ? AND TransDate = ?";
// if(!searchText.equals("")){
// strQuery += "AND (BonRokokID LIKE %'"+ searchText +"'% ) ORDER BY TransDate DESC, BonRokokID Desc";
// }
ArrayList<BonRokokModel> listRokok = new ArrayList<BonRokokModel>();
Cursor mCursor = bonRokok.rawQuery(strQuery, new String[]{userGUID, transDate});
if(mCursor.moveToFirst()){
do {
BonRokokModel dataRokok = new BonRokokModel();
dataRokok.setBonRokokID(mCursor.getString(mCursor.getColumnIndex("BonRokokID")));
dataRokok.setTransDate(mCursor.getString(mCursor.getColumnIndex("TransDate")));
dataRokok.setGudang(mCursor.getString(mCursor.getColumnIndex("Gudang")));
dataRokok.setStatus(mCursor.getString(mCursor.getColumnIndex("Status")));
listRokok.add(dataRokok);
} while (mCursor.moveToNext());
}
bonRokok.close();
return listRokok;
}
My Code to Get Data
public void settingTabItem() {
listView = (ListView) findViewById(R.id.bonRokokMain_lvMain);
listviewMain = new ArrayList<BonRokokModel>();
listviewMain = bonRokokDAO.getBonRokokList(Main_Login.userGUID,utilities.convertDateToDateDBString(edt1.getText().toString()),"");
BonRokokListAdapter adapter = new BonRokokListAdapter(this, listviewMain);
listView.setAdapter(adapter);
}
thank you
Try like this ..
ListArrayAdapter.clear(); // Clear your adapter
ListArrayAdapter.addAll(newItems); // Add new items to it
ListArrayAdapter.notifyDataSetChanged(); // Notify List view for new items in list
OR
You can update list view like this also ..
Firstly clear your list view ..
Then after fetching new data .... create complete list view again.
Use the logical of method settingTabItem() on refresh method. Don't forget to clear the listview. Sorry for the bad English.
I'm trying to put different images (.jpg , .png) dynamically into a ListView from res/drawable .
The names from the images I get from a database.
The images themselves are in the res/drawable folder.
This is what I already have, With an error of :D
String imgName; --> There are the img names I need from the database
Drawable drawable;
drawable = Class.appContext.getResources().getDrawable(Class.appContext.getResources().getIdentifier("com.example.test:drawable/"+imgName,null,null));
Then I get this into a ArrayList for every image in the database(+- 200 images):
ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map = new HashMap<String, Object>();
map = new HashMap<String, Object>();
map.put("img",drawable);
map.put("text", "some text");
list.add(map);
Now I RETURN list;
In the Class where I call :
listReceive = Class.getImages(appContext);
listSetup = new SimpleAdapter(this, listReceive, R.layout.row,
new String[] {"img", "text"}, new int[] {R.id.IMG_CELL, R.id.TEXT_CELL});
lvList.setAdapter(listSetup);
XML row is an ImageView and a TextView.
System.out :resolveUri failed on bad
bitmap uri:
android.drawable.bitmapDrawable#405739
resolveUri failed on bad bitmap uri:
android.drawable.bitmapDrawable#405639
resolveUri failed on bad bitmap uri:
android.drawable.bitmapDrawable#405959
resolveUri failed on bad bitmap uri:
android.drawable.bitmapDrawable#405677...
... ...
I got it working when I saved images into local or SDcard memory, and then put the path inside the arraylist like:
map.put("img","/data/data/com.example.test/images/" + imgName);
I can't use this because then i will need to copy the pictures from res/drawable onto local or SD.This takes up 2 times the memory. can't have that of.
There must be a way to get images dynamically from the drawable.
Any one knows what I'm missing here?
Thanks.
The SimpleAdapter expects an integer or string that specifies a resource or image URI:
public void setViewImage (ImageView v, String value)
Since: API Level 1
Called by bindView() to set the image for an ImageView but only if there is no existing ViewBinder or if the existing ViewBinder cannot handle binding to an ImageView. By default, the value will be treated as an image resource. If the value cannot be used as an image resource, the value is used as an image Uri. This method is called instead of setViewImage(ImageView, int) if the supplied data is not an int or Integer.
I believe should use setViewBinder to provide a ViewBinder for the SimpleAdapter to handle binding the Drawable data to the ImageView. Basically, the setViewValue() method should return false unless it is called for your image view. When it is called for your image view, the method should set the data in the view and return true. The return value indicates whether the ViewBinder was able to set the view or whether the adapter should try to bind the data itself via its default behavior.
Maybe something like:
private final SimpleAdapter.ViewBinder mViewBinder =
new SimpleAdapter.ViewBinder() {
#Override
public boolean setViewValue(
final View view,
final Object data,
final String textRepresentation) {
if (view instanceof ImageView) {
((ImageView) view).setImageDrawable((Drawable) data);
return true;
}
return false;
}
};
...
listSetup .setViewBinder(mViewBinder);
I've done a similar thing with a SimpleCursorAdapter and its ViewBinder.
The simple adapter expects String in the map. But your map contains a drawable for the key "img".
Instead try extending the BaseAdapter class. Override the getView method and then manually set the drawable to the imageview and text to textview.
Another thought, it might not be a good idea to keep every drawable in memory. Rather get the drawable dynamically while rendering that particular row of the list
This following method or way also works - if anyone is still interested
List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
Integer flags[] = new Integer[listdetail.size()];
Resources resources = getResources();
for (int i = 0; i < listdetail.size(); i += 2) {
listName = listdetail.get(i).getName();
int dynamicId = resources.getIdentifier(listdetail.get(i).getName().replaceAll(" ", "").toLowerCase(), "drawable", getActivity().getPackageName());
if(dynamicId == 0) {
dynamicId = R.drawable.test_image;
flags[i] = dynamicId;
}else {
flags[i] = dynamicId;
}
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("value1", listdetail.get(i).getName());
hm.put("valueImage1", Integer.toString(flags[i]));
list.add(hm);
}
String[] from = { "value1", "valueImage1"};
int[] to = { R.id.textview, R.id.imageView };
SimpleAdapter adapter = new SimpleAdapter(getActivity().getBaseContext(), list, R.layout.listlayout, from, to);
ListView listView = (ListView) getActivity().findViewById(R.id.listview);
listView.setAdapter(adapter);