0

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));
    }
kbu
  • 389
  • 2
  • 4
  • 19

1 Answers1

0

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

Community
  • 1
  • 1
vzoha
  • 151
  • 5
  • Thx, i will try and then write back – kbu Oct 09 '14 at 12:14
  • I read your code, but I can`t see code of your Adapter and method getView. In my case the problem was there and I had to use new thread for loading images in this method. – vzoha Oct 09 '14 at 12:21
  • i've added changes above. – kbu Oct 09 '14 at 12:33
  • You can try to log timestamp before and after: `Bitmap bmp = BitmapFactory.decodeByteArray(p.getProductImageBitmap(), 0, p.getProductImageBitmap().length); holder.list_image.setImageBitmap(bmp);`. If it takes too long (more than 100 ms) the new thread for this code could help. – vzoha Oct 09 '14 at 12:58
  • With AsyncTask in adapter for Bitmap bmp = BitmapFactory.decodeByteArray(p.getProductImageBitmap(), 0, p.getProductImageBitmap().length); holder.list_image.setImageBitmap(bmp); --- doesn't work properly. Images are jumping when listview scrolls – kbu Oct 09 '14 at 13:29
  • And scrolling is smooth? Problem with jumping images can be caused by recycling views. Try to add placeholder image before AsyncTask runs. For example `holder.list_image.setImageResource(R.drawable.ic_launcher);`. If it will work you can change this by preloader or something. – vzoha Oct 09 '14 at 13:59
  • yes, scrolling is smooth now. What do you mean with preloader. Method in AsyncTask or what? and in which place move holder.list_image? here is my adapter http://pastebin.com/urS2k2ct – kbu Oct 09 '14 at 14:08
  • You should put `holder.list_image.setImageBitmap(bmp);` into onPostExecute(), but that is not the problem. Try to add `holder.list_image.setImageResource(R.drawable.ic_launcher);` into onPreExecute() or before AsyncTask. With preloader i meant e.g. image with indefinite loading (similar to Android ProgressBar). – vzoha Oct 09 '14 at 14:15
  • Yes, right now works almost perfect(with PRE and POST), but my listview is draggable and when item was dragged image is jumping. Is is possible fix it? Thx. – kbu Oct 09 '14 at 14:22
  • Sorry, I have no experience with draggable listView. :( But I think, that it is similar problem to previous. Try to study how convertView works in listView works and also try to logCat getView method. Maybe empty (preloader) image should be added somewhere. – vzoha Oct 09 '14 at 14:31