Search code examples
androidlistviewandroid-camerascrollviewandroid-gallery

Android ListView with custom images from Camera or Gallery


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));
    }

Solution

  • 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