Background
Hi, I'm relatively new to android programming. I'm still learning.
I have a database with two tables : Item and Type. Item has the following columns : _id, code, type. Type has the following columns : _id, name.
In my activity, I display a spinner of the types, and a gridview of the items. The spinner is supposed to filter the results and update the gridview.
The gridview items only consist in images. The column code gives the name of the image file.
I have a database helper DatabaseHelper, that opens, close, and access the database in different ways.
At the start of my activity ItemList, getContents() is called, getContents() calls the database, and updates listOfIds and listOfCodes according to the value of selectedType (initialised to 0). Then the gridview is created.
For the gridview, I had memory issues while displaying images and scrolling (out of memory). So I followed the Android tutorial : https://developer.android.com/training/displaying-bitmaps/process-bitmap.html Now, each item is displayed asynchronously. And it works fine.
Issue
When I select a type in the spinner, selectedType is updated, getContents() is called and then notifyDataSetChanged() is called. Sometimes it works fine, sometimes it crashes. I think that when I select a type, old threads that have not been terminated are accessing listOfIds and listOfCodes while they are being updated.
How to kill all of thoses threads (before updating list) and prevent the gridview from calling getView during the update?
I was thinking about creating a list containing the threads, update it, and kill all threads before updating the lists. But I can't find the right way to prevent the gridview's adapter from creating views during the update.
Thank you for your help.
Code
Here are parts of my code :
ItemList.java
public class ItemList extends Activity {
private ImageAdapter mAdapter;
private Bitmap mPlaceHolderBitmap;
private DatabaseHelper myHelper;
public static int column = 3;
public static int MARGIN = 5; //margin in dp
public static int width;
public GridView gallery;
public SpinnerAdapter spinnerAdapter;
public Spinner mySpinner;
public int defaultImageID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.item_list);
initialiseValues();
createHelper();
resetPositions();
getContents();
createLists();
createSpinnerListener();
}
#Override
protected void onResume() {
super.onResume();
}
public void createHelper() {
myHelper = new DatabaseHelper(getApplicationContext());
myHelper.getSpinnerIds();
}
public void initialiseValues(){
myHelper.selectedType = 0;
//determines the width of the displayed image
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
width = size.x - dpToPx(2*column*MARGIN);
//determines the default image to display
defaultImageID = ...; //here I get the id of the default image;
mPlaceHolderBitmap = decodeSampledBitmapFromResource(getResources(), defaultImageID, null, null), width, width);
}
public void createList() {
spinnerAdapter = new SpinnerAdapter(this, myHelper, myHelper.spinnerNames);
spinnerAdapter.setDropDownViewResource(R.layout.spinner_item);
mySpinner = (Spinner) findViewById(R.id.spinner);
mySpinner.setAdapter(spinnerAdapter);
gallery = (GridView) findViewById(R.id.gallery);
mAdapter = new ImageAdapter(this);
gallery.setAdapter(mAdapter);
}
public void getContents() {
myHelper.getLists();
}
public void createSpinnerListener() {
mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
myHelper.selectedType = myHelper.spinnerIds.get(position);
getContents();
mAdapter.notifyDataSetChanged();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
private class ImageAdapter extends BaseAdapter {
private final Context mContext;
public ImageAdapter(Context context) {
super();
mContext = context;
}
#Override
public int getCount() {
return myHelper.listOfCodes.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup container) {
View view;
if (convertView == null) { // if it's not recycled, initialize some attributes
LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.grid_item, null);
}
else {view = convertView;}
int image_id = getApplicationContext().getResources().getIdentifier(".../" + myHelper.listOfCodes.get(position), null, null);
if (image_id == 0) {image_id = R.drawable.item_unknown;}
loadBitmap(image_id, imageView);
return view;
}
}
public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
if (bitmapData != data) {
bitmapWorkerTask.cancel(true); // Cancel previous task
}
else {
return false; // The same work is already in progress
}
}
return true; // No task associated with the ImageView, or an existing task was cancelled
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(View view) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, width, width);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper{
...
public int currentID = 0;
public int numberOfItems = 0;
public List<Integer> listOfIds = new ArrayList<>();
public List<String> listOfCodes = new ArrayList<>();
public List<Integer> spinnerIds = new ArrayList<>();
public List<String> spinnerNames = new ArrayList<>();
public int selectedType = 0;
private Context context;
public SQLiteDatabase myDataBase;
...
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.context = context;
}
...
public void getSpinnerIds() {
spinnerIds.clear();
spinnerNames.clear();
/*
updates spinnerIds by accessing the db, first one is "All types"
*/
}
public void getLists() {
listOfIds.clear();
listOfCodes.clear();
numberOfItems = 0;
String where = " WHERE code <> '0'";
if (selectedPosition != 0) {where += " AND Type = " + String.valueOf(selectedType);}
openDataBase();
try {
Cursor myCursor = myDataBase.rawQuery("SELECT _id, code, type FROM Item" + where + " ORDER BY _id ASC", null);
myCursor.moveToFirst();
do {
listOfIds.add(myCursor.getInt(0));
listOfCodes.add(myCursor.getString(1));
}
while (myCursor.moveToNext());
myCursor.close();
numberOfItems = listOfIds.size();
}
catch (Exception CursorIndexOutOfBoundsException) {}
myDataBase.close();
}
public String getName(int id) {
String name;
openDataBase();
Cursor myCursor = myDataBase.rawQuery("SELECT name FROM Item WHERE _id = " + String.valueOf(id), null);
myCursor.moveToFirst();
name = myCursor.getString(0);
myCursor.close();
myDataBase.close();
return name;
}
...
}
SpinnerAdapter.java
public class SpinnerAdapter extends ArrayAdapter<String> {
private Context context;
private DatabaseHelper helper;
public SpinnerAdapter(Context context, DatabaseHelper helper, List<String> list) {
super(context, R.layout.spinner_item_small, R.id.text, list);
this.context = context;
this.helper = helper;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView);
}
#Override
public View getView(int position, View convertView, ViewGroup prnt) {
return getCustomView(position, convertView);
}
public View getCustomView(int position, View convertView) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.spinner_item_small, null);
}
else {view = convertView;}
TextView textView = (TextView) view.findViewById(R.id.text);
textView.setText(helper.spinnerNames.get(position));
return view;
}
}
Related
I am displaying all images from storage of device in my app.The problem i am having is that when I am scrolling its not smooth.Its stucking inbetween.I am using etsy/AndroidStaggeredGrid and using below code.I tried putting tasks inside Async background Task but still i am having this issue. Why is it stucking inbetween and how can i resolve this?
GalleryFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_gallery, container, false);
galleries=new ArrayList<Gallery>();
((ActionBarActivity) getActivity()).getSupportActionBar().show();
gridview = (StaggeredGridView)rootView.findViewById(R.id.gridcamera);
progressBar=(ProgressBar)rootView.findViewById(R.id.progressBar);
gallerylist=new CameraGalleryAdapter(getActivity().getApplicationContext(), R.layout.gallery_grid,galleries);
gridview.setAdapter(gallerylist);
AsyncTaskRunner runner = new AsyncTaskRunner();
runner.execute();
return rootView;
}
private class AsyncTaskRunner extends AsyncTask<String, String, Void> {
private String resp;
#Override
protected Void doInBackground(String... params)
{
int count,count2,count3,count4;
((CameraActivity)getActivity()).setOnBackPressedListener(new BaseBackPressedListener(getActivity(),true));
final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID,MediaStore.Video.Media.DATA,
};
final String orderBy = MediaStore.Images.Media._ID;
Cursor cursor = getActivity().getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null,
null, orderBy);
count = cursor.getCount();
Cursor cursor2 = getActivity().getContentResolver().query(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, columns, null,
null, orderBy);
count2 = cursor2.getCount();
Cursor cursor3 = getActivity().getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, columns, null,
null, orderBy);
count3 = cursor3.getCount();
Cursor cursor4 = getActivity().getContentResolver().query(
MediaStore.Video.Media.INTERNAL_CONTENT_URI, columns, null,
null, orderBy);
count4 = cursor4.getCount();
for (int i = 0; i < count; i++)
{
cursor.moveToPosition(i);
int dataColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
Gallery ga=new Gallery();
ga.setImageUrl(cursor.getString(dataColumnIndex));
ga.setIsImage(true);
galleries.add(ga);
}
for (int i = 0; i < count2; i++)
{
cursor2.moveToPosition(i);
int dataColumnIndex = cursor2.getColumnIndex(MediaStore.Images.Media.DATA);
Gallery ga=new Gallery();;
ga.setImageUrl(cursor2.getString(dataColumnIndex));
ga.setIsImage(true);
galleries.add(ga);
}
for (int i = 0; i < count3; i++)
{
cursor3.moveToPosition(i);
int dataColumnIndex = cursor3.getColumnIndex(MediaStore.Video.Media.DATA);
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(cursor3.getString(dataColumnIndex),
MediaStore.Images.Thumbnails.MINI_KIND);
Gallery ga=new Gallery();
ga.setVideoThumbnail(thumb);
ga.setVideoUrl(cursor3.getString(dataColumnIndex));
ga.setIsImage(false);
galleries.add(ga);
}
for (int i = 0; i < count4; i++)
{
cursor4.moveToPosition(i);
int dataColumnIndex = cursor4.getColumnIndex(MediaStore.Video.Media.DATA);
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(cursor4.getString(dataColumnIndex),MediaStore.Images.Thumbnails.MINI_KIND);
Gallery ga = new Gallery();
ga.setVideoThumbnail(thumb);
ga.setVideoUrl(cursor4.getString(dataColumnIndex));
ga.setIsImage(false);
galleries.add(ga);
}
return null;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
gallerylist.notifyDataSetChanged();
progressBar.setVisibility(View.GONE);
}
#Override
protected void onProgressUpdate(String... text) {
}
}
CameraGalleryAdapter
public class CameraGalleryAdapter extends BaseAdapter {
ArrayList<Gallery> itemList;
LayoutInflater vi;
int Resource;
ViewHolder holder;
Bitmap bm;
public CameraGalleryAdapter(Context context, int resource, ArrayList<Gallery> objects) {
vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Resource = resource;
itemList = objects;
}
#Override
public int getCount() {
return itemList.size();
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null) {
holder = new ViewHolder();
v = vi.inflate(Resource, null);
holder.imageview = (ImageView) v.findViewById(R.id.ivImageg);
holder.playicon= (ImageView) v.findViewById(R.id.galleryplay);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
if(itemList.get(position).getIsImage())
{
holder.playicon.setVisibility(View.INVISIBLE);
AsyncTaskRunner runner = new AsyncTaskRunner();
runner.execute(itemList.get(position).getImageUrl());
bm= null ;
try {
bm = runner.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
holder.imageview.setImageBitmap(bm);
}
else
{
holder.playicon.setVisibility(View.VISIBLE);
holder.imageview.setImageBitmap(itemList.get(position).getVideoThumbnail());
}
return v;
}
static class ViewHolder {
public ImageView imageview;
public ImageView playicon;
}
private class AsyncTaskRunner extends AsyncTask<String, String,Bitmap>
{
private String resp;
#Override
protected Bitmap doInBackground(String... params)
{
resp=params[0];
Bitmap bm = decodeSampledBitmapFromUri(resp, 220, 220);
return bm;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(Bitmap aVoid) {
super.onPostExecute(aVoid);
}
#Override
protected void onProgressUpdate(String... text) {
}
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight)
{
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
its a common issue bcoz of large Image size is loading in a view each time you scroll .... to over come this issue you can try lazy Loading imgae technique.. http://androidexample.com/Download_Images_From_Web_And_Lazy_Load_In_ListView_-_Android_Example/index.php?view=article_discription&aid=112&aaid=134
I would like to load all image from gallery to activity with asynctask. I learn it from this link. But there was a problem that I was unable to solve yet.
When I scroll down slowly from the grid view it work perfectly fine. But When I scroll up or scroll faster the Image view either will load previous loaded image then only loaded the correct photo or it might loaded few photo randomly eventually only get to the correct photo.
Here is my source code
public class PhotoPicker extends ActionBarActivity {
ArrayList<String> mArrayList = new ArrayList<String>();
ImageAdapter myImageAdapter;
AsyncTaskLoadFiles myAsyncTaskLoadFiles;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo_picker);
Context context = getApplicationContext();
final GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);
myAsyncTaskLoadFiles = new AsyncTaskLoadFiles(myImageAdapter);
myAsyncTaskLoadFiles.execute();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_photo_picker, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public class AsyncTaskLoadFiles extends AsyncTask<Void, String, Void> {
ImageAdapter myTaskAdapter;
Context context = getApplicationContext();
Cursor cur;
public AsyncTaskLoadFiles(ImageAdapter adapter) {
myTaskAdapter = adapter;
}
#Override
protected void onPreExecute() {
String[] projection = new String[]{
MediaStore.Images.Media.DATA
};
// Get the base URI for the People table in the Contacts content provider.
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
// Make the query.
cur = context.getContentResolver().query(images,
projection, // Which columns to return
null, // Which rows to return (all rows)
null, // Selection arguments (none)
MediaStore.MediaColumns.DATE_ADDED + " DESC" // Ordering
);
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
if (cur.moveToFirst()) {
String bucket;
int bucketColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATA);
do {
bucket = cur.getString(bucketColumn);
publishProgress(bucket);
if (isCancelled()) break;
} while (cur.moveToNext());
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
myTaskAdapter.add(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(Void result) {
/*myTaskAdapter.notifyDataSetChanged();*/
super.onPostExecute(result);
}
}
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
void remove(int index){
mArrayList.remove(index);
}
public void add(String path){
mArrayList.add(path);
}
public int getCount() {
return mArrayList.size();
}
public Object getItem(int position) {
return mArrayList.get(position);
}
public long getItemId(int position) {
return 0;
}
class ViewHolder {
ImageView image;
int position;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(230, 230));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(2, 2, 2, 2);
convertView = imageView;
holder = new ViewHolder();
holder.image = imageView;
holder.position = position;
convertView.setTag(holder);
} else {
((ImageView)convertView).setImageBitmap(null);
holder = (ViewHolder) convertView.getTag();
}
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
Bitmap bm = decodeSampledBitmapFromUri(mArrayList.get(position), 220, 220);
Log.d("holder", String.valueOf(position));
return bm;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
//Not work for me!
v.image.setImageBitmap(result);
}
}.execute(holder);
//imageView.setImageBitmap(bm);
//return imageView;
return convertView;
}
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth,
int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height
/ (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
}
Apologies if my english make it confuse the link that I provide have 2 youtube video the second video also have the problem that I mention earlier.Any help really very grateful
It is because GridView (ListView, RecyclerView also) is re-using already existing views. When row view moved out of the screen GridView is re-using this view for another data.
In your example, when you call v.image.setImageBitmap(result); it may change image on view that is already used for another data item.
To fix this you should check if view is still related to your data model, e.g.
public View getView(final int position, final View convertView, ViewGroup parent) {
...
holder.image.setTag(position);
new AsyncTask<ViewHolder, Void, Bitmap>() {
...
#Override
protected void onPostExecute(Bitmap result) {
if(v.image.getTag() == position) {
v.image.setImageBitmap(result);
}
}
}
}
I am attempting to load picture files in the form of thumbnails from my internal storage to a list view. Currently, I am using a ViewHolder, but the loading is still choppy when scrolling so I am going to try to use an AsyncTask. However I can't get my head around how to structure the AsyncTask as most of the examples I've found deal with downloading from a website. I'm not even sure if I should subclass it in my BaseAdapter or in my MainActivity. I have added my baseadapter below with the unfinished AsyncTask at the bottom. How do I structure this to either: use the AsyncTask to assist the ViewHolder, or directly pass an image to AsyncTask and have it return the bitmap so the ListView will scroll smoothly?
public class ListViewAdapter extends BaseAdapter {
private static final int WIDTH = 250;
private static final int HEIGHT = 250;
private static final int ROTATION = 90;
private final static String TAG = "Pictures";
private final ArrayList<SelfieObject> mItems = new ArrayList<SelfieObject>();
private Context mContext;
private File mStorageDir;
private String mFilePrefix;
public ListViewAdapter(Context context, File storageDir, String filePrefix) {
mContext = context;
mStorageDir = storageDir;
mFilePrefix = filePrefix;
//get file list from storage to display
InitializeItemsFromStorage(storageDir, filePrefix);
}
//this method creates an array of files stored on the device or SD card.
private void InitializeItemsFromStorage(File storageDir, String prefix) {
log("in InitializeItemsFromStorage()");
mItems.clear();
File[] files = getFiles(storageDir, prefix);
for (File f : files) {
SelfieObject selfie = new SelfieObject(f);
mItems.add(selfie);
}
}
public void Update() {
log("in Update()");
InitializeItemsFromStorage(mStorageDir, mFilePrefix);
notifyDataSetChanged();
}
/*
* return the list of file objects of the given directory that begin with
* the prefix.
*/
private File[] getFiles(File storageDir, final String prefix) {
FileFilter fileFilter = new FileFilter() {
#Override
public boolean accept(File pathname) {
if (pathname.isFile() && pathname.getName().startsWith(prefix))
return true;
else
return false;
}
};
File[] result = storageDir.listFiles(fileFilter);
return result;
}
public int getCount() {
log("in getCount()");
return mItems.size();
}
public Object getItem(int position) {
log("in getItem()");
return mItems.get(position);
}
public long getItemId(int position) {
log("in getItemId()");
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.v(TAG, "in getView for position " + position +
", convertView is " +
((convertView == null)?"null":"being recycled"));
View newView = convertView;
ViewHolder holder;
if (null == convertView) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
newView = inflater.inflate(R.layout.single_item, null);
holder = new ViewHolder();
holder.description = (TextView) newView.findViewById(R.id.textView1);
holder.picture = (ImageView) newView.findViewById(R.id.imageView1);
newView.setTag(holder);
} else {
holder = (ViewHolder) newView.getTag();
}
holder.picture.setScaleType(ImageView.ScaleType.CENTER_CROP);
SelfieObject selfie = (SelfieObject) getItem(position);
setPic(holder.picture, new Point(WIDTH, HEIGHT), selfie.getPath());
TextView textView = (TextView) holder.description;
textView.setText(selfie.getName());
log("Exiting getView");
return newView;
}
static class ViewHolder {
ImageView picture;
TextView description;
}
public void add(SelfieObject listItem) {
mItems.add(listItem);
notifyDataSetChanged();
}
public ArrayList<SelfieObject> getList(){
return mItems;
}
public void removeAllViews(){
mItems.clear();
this.notifyDataSetChanged();
}
public static void setPic(ImageView imageView, Point requestedSize,
String pathName) {
// set the dimensions of the View
int targetW = requestedSize.x;
int targetH = requestedSize.y;
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(pathName, bmOptions);
imageView.setImageBitmap(bitmap);
imageView.setRotation(ROTATION);
}
//Automation logging tool
public void log(String s){
Log.i(TAG, s);
}
private class AsyncTaskLoadImage extends AsyncTask<Object, Void, Bitmap>{
private ImageView image;
private String path;
public AsyncTaskLoadImage(ImageView image){
this.image = image;
this.path = image.getTag().toString();
}
#Override
protected Bitmap doInBackground(Object... params) {
Bitmap bitmap = null;
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + path);
if(file.exists()){
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
}
}
The AsyncTask should do whatever is too slow to do in the UI thread. In this example, fetching and downsampling the image, and setting up the ViewHolder should be done in the background.
However, I suggest you do not try and fix the ListView by yourself, but rather have a look at already existing solutions, like: https://github.com/lucasr/smoothie
Also, I highly suggest you downsample your bitmaps, otherwise they will consume a lot of excess computing time and memory. While the previous can lag your UI when scrolling, the latter will get you a nice OutOfMemoryException. See: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
I'm using ViewPager with Action Bar. On the Action Bar there are some component, for example delete button. My application is a gallery app, and I would like to delete the selected pictures. It's works, but I don't know how to refresh my fragment in order to disappear deleted pictures from gridview.
I try to make it for a long time, and It would be very important for me.
Thanks for your help.
MainActivity:
public class MainActivity extends FragmentActivity {
private static final int FOLDER = 0;
private static final int TEXT_ID = 0;
private static final String PATH_OF_FILES = "/Pictures/";
File folder;
ViewPager Tab;
TabPagerAdapter TabAdapter;
ActionBar actionBar;
ArrayList<ImageItem> listOfFiles = new ArrayList<ImageItem>();
private String dirToMove = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listOfFiles = FileListerUtil.getFilesFromDirectory(PATH_OF_FILES);
TabAdapter = new TabPagerAdapter(getSupportFragmentManager());
Tab = (ViewPager) findViewById(R.id.pager);
Tab.setAdapter(TabAdapter);
Tab.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar = getActionBar();
actionBar.setSelectedNavigationItem(position);
}
});
Tab.setAdapter(TabAdapter);
actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabReselected(android.app.ActionBar.Tab tab,
FragmentTransaction ft) {
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
Tab.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(android.app.ActionBar.Tab tab,
FragmentTransaction ft) {
}
};
actionBar.addTab(actionBar.newTab().setText("Pic")
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab().setText("Other")
.setTabListener(tabListener));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.layout.activity_main_action, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_remove:
for (int i = 0; i < listOfFiles.size(); i++) {
if (listOfFiles.get(i).isChecked()) {
File file = new File(listOfFiles.get(i).getImagePath());
file.delete();
}
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public ArrayList<ImageItem> getListOfFiles() {
return listOfFiles;
}
public void setListOfFiles(ArrayList<ImageItem> listOfFiles) {
this.listOfFiles = listOfFiles;
}
public String getDirToMove() {
return dirToMove;
}
public void setDirToMove(String dirToMove) {
this.dirToMove = dirToMove;
}
public static boolean copyFile(String from, String to) {
try {
File sd = Environment.getExternalStorageDirectory();
if (sd.canWrite()) {
int end = from.toString().lastIndexOf("/");
String str1 = from.toString().substring(0, end);
String str2 = from.toString().substring(end+1, from.length());
File source = new File(str1, str2);
File destination= new File(to, str2);
if (source.exists()) {
FileChannel src = new FileInputStream(source).getChannel();
FileChannel dst = new FileOutputStream(destination).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
return true;
} catch (Exception e) {
return false;
}
}
FragmentStateAdapter:
public class TabPagerAdapter extends FragmentStatePagerAdapter {
public TabPagerAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
}
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new Pictures();
case 1:
return new Albums();
}
return null;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return 2; //No of Tabs
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Pictures:
public class Pictures extends Fragment{
private LruCache<String, Bitmap> mMemoryCache;
private int count;
private Bitmap[] thumbnails;
private String[] arrPath;
private Context context;
ArrayList<ImageItem> fileListParam = new ArrayList<ImageItem>();
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.layout.fragment_menu, menu);
}
public class ImageAdapter extends BaseAdapter {
boolean[] checkState;
private LayoutInflater inflat;
private Context mContext;
ArrayList<ImageItem> itemList = new ArrayList<ImageItem>();
public ImageAdapter(ArrayList<ImageItem> files, Context c) {
mContext = c;
inflat = (LayoutInflater) getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
this.itemList = files;
}
void clear() {
itemList.clear();
}
void remove(int index) {
itemList.remove(index);
}
#Override
public int getCount() {
return itemList.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return itemList.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
View rowView = convertView;
// reuse views
if (rowView == null) {
rowView = inflat.inflate(R.layout.galleryitem, null);
// configure view holder
final ViewHolderPictures viewHolderPictures = new ViewHolderPictures();
viewHolderPictures.imageview = (ImageView) rowView
.findViewById(R.id.thumbImage);
viewHolderPictures.checkbox = (CheckBox) rowView
.findViewById(R.id.itemCheckBox);
viewHolderPictures.imageview.setBackgroundResource(R.drawable.image_border);
viewHolderPictures.imageview.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolderPictures.imageview.setPadding(5, 5, 5, 5);
rowView.setTag(viewHolderPictures);
}
// fill data
ViewHolderPictures holder = (ViewHolderPictures) rowView.getTag();
holder.checkbox
.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton arg0,
boolean arg1) {
itemList.get(position).setChecked(arg1);
}
});
final Bitmap bm = getBitmapFromMemCache(itemList.get(position)
.getImagePath());
if (bm == null) {
BitmapWorkerTask task = new BitmapWorkerTask(holder.imageview);
task.execute(itemList.get(position).getImagePath());
}
holder.imageview.setImageBitmap(bm);
holder.checkbox.setChecked(itemList.get(position).isChecked());
return rowView;
}
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth,
int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage
// collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 140,
120);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView) imageViewReference
.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
int fragVal;
ImageAdapter myImageAdapter;
static Pictures init(int val) {
Pictures truitonFrag = new Pictures();
// Supply val input as an argument.
Bundle args = new Bundle();
args.putInt("val", val);
truitonFrag.setArguments(args);
return truitonFrag;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragVal = getArguments() != null ? getArguments().getInt("val") : 1;
setHasOptionsMenu(true);
final int memClass = ((ActivityManager) getActivity().getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number
// of items.
return bitmap.getByteCount();
}
};
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Gridview gridview;
View layoutView = inflater.inflate(R.layout.kepgrid, container, false);
gridview = (GridView) layoutView.findViewById(R.id.gv_photolist);
ArrayList<ImageItem> files = ((MainActivity) getActivity())
.getListOfFiles();
context = getActivity();
myImageAdapter = new ImageAdapter(files, context);
gridview.setAdapter(myImageAdapter);
return layoutView;
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mMemoryCache.get(key);
}
}
Here I am getting stored picture paths from the database. So that I am showing the images in the gridview with the help of paths. But as it is shown in my code below, I am loading cursoradapter in the asynctask. The problem here is for the first time it is okay that it takes some time until all images are loaded.
But if user adds one more pic to the database, it again takes much time to load all images as I kept the asynctask in onResume() to make the added picture also visible. The same is the case whenever user adds each picture. Can someone suggest me a simple way to get the last added picture visible in the gridview without having to load all the pictures again. Or in the least case can some one help me implementing lazy adapter to the present cursoradapter if that is not possible?
public class ImagesScreen extends Activity {
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.images_screen);
gridView = (GridView)findViewById(R.id.gridView1);
String[] from = new String[] { Reminders.MOM_PATHS };
int[] to = new int[] {};
dbCon = new SQLiteConnectClass(ImagesScreen.this);
imageCursorAdapter = new ImageCursorAdapter(ImagesScreen.this, R.layout.griditem, null, from, to);
gridView.setAdapter(imageCursorAdapter);
}
protected void onResume() {
super.onResume();
new GetImages().execute((Object[]) null);
}
private class GetImages extends AsyncTask<Object, Object, Cursor> {
#Override
protected Cursor doInBackground(Object... params) {
return dbCon.getAllImages();
}
#Override
protected void onPostExecute(Cursor result) {
imageAdapter.changeCursor(result);
}
public void addpictures(View view){
// code for adding picture paths to the database by transferring to another activity.
}
}
CustomAdapter class:
public class ImageCursorAdapter extends SimpleCursorAdapter{
private int layout;
private LayoutInflater mLayoutInflater;
private Context mContext;
public ImageAdapter(Context context, int layout, Cursor c,String[] from, int[] to) {
super(context, layout, c, from, to,0);
this.layout = layout;
mLayoutInflater=LayoutInflater.from(context);
mContext = context;
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mLayoutInflater.inflate(layout, parent, false);
return v;
}
public void bindView(final View v, final Context context, Cursor c) {
final int id = c.getInt(c.getColumnIndex("id"));
final String imagepath = c.getString(c.getColumnIndex("paths"));
ImageView imageview = (ImageView)v.findViewById(R.id.imageView1);
File imgFile = new File(imagepath);
if(imgFile.exists()){
Bitmap imageBitmap = decodeFile(imgFile);
imageview.setImageBitmap(imageBitmap);
}
}
}
use this code
public class Imagegallery extends Activity implements OnItemClickListener,
OnScrollListener, OnTouchListener {
Button btn;
SQLiteDatabase sampleDB = null;
String SAMPLE_DB_NAME = "hic";
int size = 0;
TextView headertext;
Button cartbtn;
ImageView footer1, footer2, footer3, footer4, footer5;
GridView gridView;
boolean larg = false;
String products;
int j = 1;
int ii = 0;
String imagepath[];
String productname[];
int productid[] = new int[1000];
String productids[] = new String[1000];
int integerprodids[] = new int[1000];
final Context context = this;
String filename2[];
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.currentspecial);
File root = new File(Environment.getExternalStorageDirectory()
+ File.separator + "aiwhic/product" + File.separator);
File[] fileName = root.listFiles();
filename2 = new String[fileName.length];
for (int j = 0; j < fileName.length; j++) {
Uri uri = Uri.fromFile(fileName[j]);
filename2[j] = fileName[j].getAbsolutePath();
Log.e("file", filename2[j]);
}
gridView = (GridView) findViewById(R.id.gridView1);
gridView.setAdapter(new ImageAdapter(this, filename2, filename2));
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
int isf = filename2[position].lastIndexOf("/");
int laf = filename2[position].lastIndexOf(".");
String filename = filename2[position].substring(isf + 1, laf);
int pos = Integer.parseInt(filename);
Intent go = new Intent(Imagegallery.this, ItemsPage.class);
Bundle bundle = new Bundle();
bundle.putInt("position", pos);
bundle.putString("whichclass", "imagegallery");
bundle.putInt("catid", 2);
go.putExtras(bundle);
startActivity(go);
}
});
}
public class ImageAdapter extends BaseAdapter {
private Context context;
private final String[] mobileValues;
private final String[] mobileimages;
public ImageAdapter(Context context, String[] mobileValues, String[] mo) {
this.context = context;
this.mobileValues = mobileValues;
this.mobileimages = mo;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
gridView = inflater.inflate(R.layout.currentspeciallist, null);
TextView textView = (TextView) gridView
.findViewById(R.id.textView1);
textView.setText(mobileValues[position]);
textView.setVisibility(View.INVISIBLE);
ImageView imageView = (ImageView) gridView
.findViewById(R.id.imageView1);
imageView.setTag(mobileimages[position]);
new Loadimage().execute(imageView);
// imageView.setImageBitmap(BitmapFactory
// .decodeFile(mobileimages[position]));
} else {
gridView = (View) convertView;
}
return gridView;
}
public int getCount() {
return mobileimages.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
}
class Loadimage extends AsyncTask<Object, Void, Bitmap> {
private ImageView imv;
private String path;
#Override
protected Bitmap doInBackground(Object... params) {
imv = (ImageView) params[0];
path = imv.getTag().toString();
// Bitmap thumb = BitmapFactory.decodeFile(path);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2; // for 1/2 the image to be loaded
Bitmap thumb = Bitmap.createScaledBitmap(
BitmapFactory.decodeFile(path, opts), 120, 120, false);
return thumb;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (!imv.getTag().toString().equals(path)) {
/*
* The path is not same. This means that this image view is
* handled by some other async task. We don't do anything and
* return.
*/
return;
}
if (bitmap != null && imv != null) {
imv.setVisibility(View.VISIBLE);
imv.setImageBitmap(bitmap);
} else {
imv.setVisibility(View.GONE);
}
}
}
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
}
then create a xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:src="#drawable/ic_launcher" >
</ImageView>
</RelativeLayout>
i am getting file path directly . u just get the name from sqlite and pass the array string
use Android-Universal-Image-Loader
and set
ImageView imageview = (ImageView)v.findViewById(R.id.imageView1);
File imgFile = new File(imagepath);
if(imgFile.exists()){
imageLoader.displayImage(imageUri(ur file path), imageView);
}