0

I'm creating custom ListView. Adapter:

public class CustomListEventsAdapter extends BaseAdapter {

private Context context;
private List<Map<String, String>> values;

public CustomListEventsAdapter(Context context, List<Map<String, String>> values) {
    this.context = context;
    this.values = values;
}

@Override
public int getCount() {
    return values.size();
}

@Override
public Object getItem(int position) {
    return values.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    final CustomItemForEventsList view = new CustomItemForEventsList(context, values.get(position), (long) position);

    int itemHeight;
    if (parent.getWidth() > parent.getHeight()) {
        itemHeight = parent.getHeight();
    } else {
        itemHeight = parent.getHeight() / 2;
    }

    view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, itemHeight));
    return view;
}}

Custom View for ListView's item:

public class CustomItemForEventsList extends View {

private MainActivity context;
private Map<String,String> values;
private long id;

private Paint pMainText;

private int viewWidth;
private int viewHeight;
private boolean isLandscape;

private Bitmap backgroundBitmap;
private RectF bitmapSizes;
private RectF viewSizes;
private Matrix matrix;

public CustomItemForEventsList(final Context context, Map<String,String> values, long id) {
    super(context);
    this.context = (MainActivity) context;
    this.values = values;
    this.id = id;

    pMainText = new Paint(Paint.ANTI_ALIAS_FLAG);
    pMainText.setColor(Color.WHITE);
    pMainText.setTextAlign(Paint.Align.LEFT);

    bitmapSizes = new RectF();
    viewSizes = new RectF();
    matrix = new Matrix();
    loadBackgroundBitmap(values.get("url"));
}

private void loadBackgroundBitmap(final String url) {
    context.imageLoader.loadImage(url, new ImageSize(viewWidth, viewHeight), context.displayImageOptions, new ImageLoadingListener() {
        @Override
        public void onLoadingStarted(String s, View view) {
        }

        @Override
        public void onLoadingFailed(String s, View view, FailReason failReason) {
            // Log.d(Statics.LOG, "onLoadingFailed");
        }

        @Override
        public void onLoadingComplete(String s, View view, Bitmap bitmap) {
            backgroundBitmap = bitmap;
            invalidate();
        }

        @Override
        public void onLoadingCancelled(String s, View view) {
            // Log.d(Statics.LOG, "onLoadingCanceled");
            loadBackgroundBitmap(url);
        }
    });
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    viewWidth = MeasureSpec.getSize(widthMeasureSpec);
    viewHeight = MeasureSpec.getSize(heightMeasureSpec);
    isLandscape = viewWidth > viewHeight;

    viewSizes.set(0, 0, viewWidth, viewHeight);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawARGB(255, 0, 0, 0);
    // Drawing view's background
    if (backgroundBitmap != null) {
        if (!backgroundBitmap.isRecycled()) {
            bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
            matrix.setRectToRect(viewSizes, bitmapSizes, Matrix.ScaleToFit.FILL);
            matrix.invert(matrix);
            canvas.drawBitmap(backgroundBitmap, matrix, null);
        }
    }
}}

Everything works perfect on new Android's versions (tested on 4.0, 4.2, 4.4), but in 2.3.3 appears troubles with positioning and resizing of ListView items: bitmaps don't resize to view's sizes in onDraw, when we click on ListView or scroll it and release.

Screens from 2.3.3. That's what happening when we touch down ListView, then scroll, then wait for a while and release (that behavior I want to see in all cases): http://s16.postimg.org/qvf26fb45/image.png

And here is what usually happens, when we click on ListView or scroll and release: http://s22.postimg.org/d7fd8quc1/image.png

Why does it happen and how can we fix that?

Andrei K.
  • 534
  • 7
  • 20
  • swap RectFs to matrix.setRectToRect(bitmapSizes, viewSizes, ... and remove matrix.invert() method call – pskink Aug 15 '14 at 18:12
  • Thank you so much, @pskink! It solved the problem. But I can't understand why drawing mechanism between 4.0 and 2.3.3 has differences such like this and why did this problem occur? – Andrei K. Aug 16 '14 at 06:17
  • UPD: sorry, it didn't solve it in fact. I saw, that when we use matrix.setRectToRect with flag Matrix.ScaleToFit.FILL, our image stretches to view and its scales becomes wrong: http://s12.postimg.org/y3u04k225/image.png And when we us Matrix.ScaleToFit.START or others left, bitmap fits view: http://s11.postimg.org/rrwbavhrn/image.png But I want bitmap to upscale to view with saving its proportion (parts of bitmap on its sides will cropped). Is it possible? – Andrei K. Aug 16 '14 at 06:34
  • Matrix.ScaleToFit.FILL doesn't preserve aspect ratio, you probably want start, end or center – pskink Aug 16 '14 at 06:44
  • Looks like we didn't understand each other) I don't want to put Bitmap inside View, my want is to upscale Bitmap to View and put it in a center of View (some parts of Bitmap will crop). And I already found a solution. – Andrei K. Aug 16 '14 at 11:29
  • Matrix.ScaleToFit.CENTER will do that and it doesn't crop any parts of your Bitmap – pskink Aug 16 '14 at 11:34
  • But I exactly want to crop it) Looks like my Russian English is not good enough or I didn't explain what I need in a proper way ;) – Andrei K. Aug 16 '14 at 11:51
  • if you want to crop it, see this: http://pastebin.com/k0Ka50j5 – pskink Aug 16 '14 at 12:39

1 Answers1

0

Ok, I found out how to solve that problem in Android 2.3.3. Of course we can check Android's version in code and make two branches: for old and new versions. For new versions use Matrix:

if (!backgroundBitmap.isRecycled()) {
        bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
        matrix.setRectToRect(viewSizes, bitmapSizes, Matrix.ScaleToFit.CENTER);
        matrix.invert(matrix);
        canvas.drawBitmap(backgroundBitmap, matrix, null);
}

And for old versions use method, proposed by @pskink in comments. But I don't want to see any differences in different Android versions (fit in old, crop in new), so I offer another way.

Our target - is to avoid operations with Rects and Matrix, which looks like are very expensive and give us some bugs with drawing custom ListView items in Android 2.3.3. So we have to do all upscale operations manually:

public class CustomItemForEventsList extends View {

private MainActivity context;

private int viewWidth;
private int viewHeight;

private Bitmap backgroundBitmap;
private Rect bitmapSizes;
private Rect preferedBitmapSizes;

public CustomItemForEventsList(final Context context, Map<String,String> values, long id) {
    super(context);
    this.context = (MainActivity) context;

    bitmapSizes = new Rect();
    preferedBitmapSizes = new Rect();
    loadBackgroundBitmap(values.get("url"));
}

private void loadBackgroundBitmap(final String url) {
    context.imageLoader.loadImage(url, new ImageSize(viewWidth, viewHeight), context.displayImageOptions, new ImageLoadingListener() {
        @Override
        public void onLoadingStarted(String s, View view) {
        }

        @Override
        public void onLoadingFailed(String s, View view, FailReason failReason) {
            // Log.d(Statics.LOG, "onLoadingFailed");
        }

        @Override
        public void onLoadingComplete(String s, View view, Bitmap bitmap) {
            backgroundBitmap = bitmap;
            invalidate();
        }

        @Override
        public void onLoadingCancelled(String s, View view) {
            // Log.d(Statics.LOG, "onLoadingCanceled");
            loadBackgroundBitmap(url);
        }
    });
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    viewWidth = MeasureSpec.getSize(widthMeasureSpec);
    viewHeight = MeasureSpec.getSize(heightMeasureSpec);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawARGB(255, 0, 0, 0);
    // Drawing view's background
    if (backgroundBitmap != null) {
        if (!backgroundBitmap.isRecycled()) {
            bitmapSizes.set(0, 0, backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
            setPreferedBitmapSizes();
            canvas.drawBitmap(backgroundBitmap, bitmapSizes, preferedBitmapSizes, null);
        }
    }
}

private void setPreferedBitmapSizes() {
    int instBmpWidth = backgroundBitmap.getWidth();
    int instBmpHeight = backgroundBitmap.getHeight();

    int newBmpWidth, newBmpHeight;
    newBmpWidth = viewWidth; newBmpHeight = viewWidth * instBmpHeight / instBmpWidth;

    if (viewHeight > newBmpHeight) {
        newBmpWidth = instBmpWidth * viewHeight / instBmpHeight; newBmpHeight = viewHeight;
        if (newBmpWidth == viewWidth) {
            preferedBitmapSizes.set(0, 0, newBmpWidth, newBmpHeight);
        } else {
            preferedBitmapSizes.set(-((newBmpWidth-viewWidth)/2), 0, viewWidth + ((newBmpWidth-viewWidth)/2), newBmpHeight);
        }
    } else {
        if (newBmpHeight == viewHeight) {
            preferedBitmapSizes.set(0, 0, newBmpWidth, newBmpHeight);
        } else {
            preferedBitmapSizes.set(0, -((newBmpHeight-viewHeight)/2), newBmpWidth, viewHeight + ((newBmpHeight-viewHeight)/2));
        }
    }
}}

Now the problem is solved:

http://s29.postimg.org/hbv9d0z3b/image.png

UPDATE: We can use much more simpler code (thanks to pskink, it is his solution):

        m.reset();
        float scaleX = getWidth() / (float) b.getWidth();
        float scaleY = getHeight() / (float) b.getHeight();

        float scale = Math.max(scaleX, scaleY);
        m.postScale(scale, scale);
        float dx = (getWidth() - b.getWidth() * scale) / 2;
        float dy = (getHeight() - b.getHeight() * scale) / 2;
        m.postTranslate(dx, dy);
       
        canvas.drawBitmap(b, m, null);

Where m - Matrix, b - our Bitmap.

Community
  • 1
  • 1
Andrei K.
  • 534
  • 7
  • 20