I making ListView with possibility add images from camera or gallery. All works fine, but scrolling works not so smooth.
here is my code:
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.context_rename:
renameDialog(selectedItem);
return true;
case R.id.context_set_foto:
selectImage();
return true;
default:
return false;
}
}
private void selectImage() {
final CharSequence[] options = { getString(R.string.takeAFoto), getString(R.string.chooseFromGallery),getString(R.string.dialogUserCancel) };
AlertDialog.Builder builder = new AlertDialog.Builder(List.this);
builder.setTitle(getString(R.string.addFoto));
builder.setItems(options, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
if (options[item].equals(getString(R.string.takeAFoto))) {
//define the file-name to save photo taken by Camera activity
String fileName = "new-photo-name.jpg";
//create parameters for Intent with filename
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DESCRIPTION,"Image captured by camera");
//imageUri is the current activity attribute, define and save it for later usage (also in onSaveInstanceState)
imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
//create new Intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
startActivityForResult(intent, 1);
}
else if (options[item].equals(getString(R.string.chooseFromGallery))) {
try {
Intent gintent = new Intent();
gintent.setType("image/*");
gintent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(gintent, "Select Picture"),
2);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),
e.getMessage(),
Toast.LENGTH_LONG).show();
Log.e(e.getClass().getName(), e.getMessage(), e);
}
}
else if (options[item].equals(getString(R.string.dialogUserCancel))) {
dialog.dismiss();
}
}
});
builder.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Uri selectedImageUri = null;
String filePath = null;
if (resultCode == RESULT_OK) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
//use imageUri here to access the image
selectedImageUri = imageUri;
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Picture was not taken", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Picture was not taken", Toast.LENGTH_SHORT).show();
}
} else if (requestCode == 2) {
selectedImageUri = data.getData();
}
if(selectedImageUri != null){
try {
// OI FILE Manager
String filemanagerstring = selectedImageUri.getPath();
// MEDIA GALLERY
String selectedImagePath = getPath(selectedImageUri);
if (selectedImagePath != null) {
filePath = selectedImagePath;
} else if (filemanagerstring != null) {
filePath = filemanagerstring;
} else {
Toast.makeText(getApplicationContext(), "Unknown path",
Toast.LENGTH_LONG).show();
Log.e("Bitmap", "Unknown path");
}
if (filePath != null) {
decodeFile(filePath);
} else {
bitmap = null;
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Internal error",
Toast.LENGTH_LONG).show();
Log.e(e.getClass().getName(), e.getMessage(), e);
}
}
}
}
public String getPath(Uri uri) {
String res = null;
String[] proj = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri, proj, null, null, null);
if(cursor.moveToFirst()){;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
res = cursor.getString(column_index);
}
cursor.close();
return res;
}
public void decodeFile(String filePath) {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 1024;
// Find the correct scale value. It should be the power of 2.
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp < REQUIRED_SIZE && height_tmp < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
bitmap = BitmapFactory.decodeFile(filePath, o2);
prod_image.setImageBitmap(bitmap);
replaceBitmapInArray(bitmap);
}
When listview item more than size of display, scrolling doesn't work smoothly. Or maybe make more strong compress for images? How will be better. And is it possible realize "Take foto from camera" without permissions in manifest for STORAGE?
Thx in advance!
/CHANGES/
i'm using this function too, for changes in Arraylist of objects
private void replaceBitmapInArray(Bitmap bitmap){
Product p = productsArray.get(selectedPosition);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
byte[] bArray = bos.toByteArray();
p.setProdImage(bArray);
productsArray.set(selectedPosition,p);
animAdapter.notifyDataSetChanged();
}
and Adapter's code:
public class MyListAdapter extends ArrayAdapter<Product> implements UndoAdapter {
private final Context mContext;
HashMap<Product, Integer> mIdMap = new HashMap<Product, Integer>();
ArrayList<Product> products = new ArrayList<Product>();
final int INVALID_ID = -1;
LayoutInflater lInflater;
public MyListAdapter(Context context, int textViewResourceId, List<Product> prod) {
//super(context, textViewResourceId, prod);
super(prod);
lInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mContext = context;
for (int i = 0; i < prod.size(); i++) {
//add(prod.get(i));
mIdMap.put(prod.get(i),i);
}
}
@Override
public long getItemId(final int position) {
//return getItem(position).hashCode();
Product item = (Product) getItem(position);
return mIdMap.get(item);
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
//TextView view = (TextView) convertView;
/*View view = convertView;
if (view == null) {
//view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
view = lInflater.inflate(R.layout.item, parent, false);
view.setBackgroundResource(R.drawable.rounded_corners);
Product p = getItem(position);
//view.setText(getItem(position).getProductName());
((TextView) view.findViewById(R.id.tvDescr)).setText(p.getProductName());
((ImageView) view.findViewById(R.id.ivImage)).setImageResource(p.getProductImage());
if(p.getProductImageBitmap() != null) {
if (p.getProductImageBitmap().length > 0) {
Bitmap bmp = BitmapFactory.decodeByteArray(p.getProductImageBitmap(), 0, p.getProductImageBitmap().length);
ImageView image = (ImageView) view.findViewById(R.id.list_image);
image.setImageBitmap(bmp);
}
}
ImageView iv = (ImageView)view.findViewById(R.id.ivImage);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MyListAdapter.this.remove(getItem(position));
Product p = getItem(position);
Toast.makeText(mContext, p.getProductName() + " " + mContext.getString(R.string.deleted_item), Toast.LENGTH_SHORT).show();
MyListAdapter.this.notifyDataSetChanged();
}
});
}
return view;*/
ViewHolder holder = null;;
Product p = getItem(position);
if (convertView == null) {
convertView = lInflater.inflate(R.layout.item, null);
convertView.setBackgroundResource(R.drawable.rounded_corners);
holder = new ViewHolder();
holder.tvDescr = (TextView) convertView.findViewById(R.id.tvDescr);
holder.list_image = (ImageView) convertView.findViewById(R.id.list_image);
holder.products_amount = (TextView) convertView.findViewById(R.id.products_amount);
holder.products_price = (TextView) convertView.findViewById(R.id.products_price);
holder.ivImage = (ImageView) convertView.findViewById(R.id.ivImage);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (p.getProductImageBitmap() != null && p.getProductImageBitmap().length > 0) {
Bitmap bmp = BitmapFactory.decodeByteArray(p.getProductImageBitmap(), 0, p.getProductImageBitmap().length);
holder.list_image.setImageBitmap(bmp);
} else {
holder.list_image.setImageResource(R.drawable.ic_launcher);
}
holder.tvDescr.setText(p.getProductName());
holder.ivImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String deletedItem = getItem(position).getProductName();
MyListAdapter.this.remove(getItem(position));
if (MyListAdapter.this.getCount() > 0) {
Toast.makeText(mContext, deletedItem + " " + mContext.getString(R.string.deleted_item), Toast.LENGTH_SHORT).show();
MyListAdapter.this.notifyDataSetChanged();
} else {
Toast.makeText(mContext,mContext.getString(R.string.sklerolist_empty), Toast.LENGTH_SHORT).show();
}
}
});
return convertView;
}
static class ViewHolder {
ImageView list_image;
TextView tvDescr;
TextView products_amount;
TextView products_price;
ImageView ivImage;
}
@NonNull
@Override
public View getUndoView(final int position, final View convertView, @NonNull final ViewGroup parent) {
View view = convertView;
if (view == null) {
//view = LayoutInflater.from(mContext).inflate(R.layout.undo_row, parent, false);
view = lInflater.inflate(R.layout.undo_row, parent, false);
}
return view;
}
@NonNull
@Override
public View getUndoClickView(@NonNull final View view) {
return view.findViewById(R.id.undo_row_undobutton);
}
and function SetAdapter:
private void setAdapter(){
final com.nhaarman.listviewanimations.ArrayAdapter<Product> adapter = new MyListAdapter(this,R.layout.text_view,productsArray);
SimpleSwipeUndoAdapter simpleSwipeUndoAdapter = new SimpleSwipeUndoAdapter(adapter, this, new MyOnDismissCallback(adapter));
animAdapter = new AlphaInAnimationAdapter(simpleSwipeUndoAdapter);
animAdapter.setAbsListView(listView);
assert animAdapter.getViewAnimator() != null;
animAdapter.getViewAnimator().setInitialDelayMillis(INITIAL_DELAY_MILLIS);
listView.setAdapter(animAdapter);
/* Enable drag and drop functionality */
listView.enableDragAndDrop();
listView.setDraggableManager(new TouchViewDraggableManager(R.id.list_row_draganddrop_touchview));
listView.setOnItemMovedListener(new MyOnItemMovedListener(adapter));
listView.setOnItemLongClickListener(new MyOnItemLongClickListener(listView));
/* Enable swipe to dismiss */
listView.enableSimpleSwipeUndo();
/* Add new items on item click */
//listView.setOnItemClickListener(new MyOnItemClickListener(listView));
}
In my opinion the main problem is that method decodeFile(String filePath)
takes to long. During scrolling system wait for it and scrolling is not smooth.
Try to run decodeFile(String filePath)
in new thread (you can use AsyncTask).
EDIT: Method getView
is called everytime you scroll, add items or refresh listView. Item is showed after getView
ends. So long running operations (more than 100 ms) can cause not smooth scrolling.
This long running operation (e.g.: decode Bitmap image) should run in new thread (AsyncTask). But because of recycling views in adapter listView can show old images before thread ends. So it is good to show some placeholder image (e.g. ProgressBar) in getView
before new thread starts.
More about this: http://developer.android.com/training/improving-layouts/smooth-scrolling.html