Intereting Posts
Android – добавление вида программно игнорирует некоторые атрибуты стиля Шрифт заголовка приложения Android изменил значение по умолчанию при изменении ориентации Добавление внешних банок в проект Android UIautomator Предупреждение: для запроса без сеанса требуется токен, но отсутствует идентификатор приложения или токен клиента Определить шрифт из папки с активными файлами в xml-файле Как изменить положение индикатора группы ExpandableListView? Изменить направление ActionBar W / ResourceType (463): Ошибка получения записи в пакете 0 (ошибка -75) в действии Android Настройка пользовательской темы в настройках списка Android Тестирование Android В приложениях Биллинг реальных покупок Как перезагрузить вкладку при изменении вкладки? Поместить постоянный текст внутри EditText, который должен быть не редактируемый – Android Отправить Эл. Почта Delphi XE5 Правильное использование Sqlite на Android (SQLiteOpenHelper, подключения, основной поток и т. Д.) Могу ли я отправить push-уведомление на телефон, у которого у меня есть только его номер?

Как получить круговой, обрезанный по центру образ, без создания нового растрового изображения?

Примечание . Я знаю, что в этом есть много вопросов и репозиториев, но ни один из них не подходит для того, что я пытаюсь достичь.

Задний план

Учитывая растровое изображение любого соотношения сторон, я хочу установить его как содержимое ImageView (используя только рисование, без расширения ImageView), так что содержимое будет обрезано по центру и все же в форме круга ,

Все это при минимальном использовании памяти, потому что иногда изображения могут быть довольно большими. Я не хочу создавать совершенно новый Bitmap только для этого. Содержимое уже есть …

Проблема

Все решения, которые я нашел, не имеют ни одной из написанных мной вещей: некоторые не центрируют, некоторые предполагают, что изображение имеет квадратную форму, некоторые создают новое растровое изображение из заданного растрового изображения …

Что я пробовал

Помимо попыток использования различных репозиториев, я пробовал этот учебник , и я попытался исправить его для случая неквадратных пропорций, но я потерпел неудачу.

Вот его код, если веб-сайт будет закрыт:

public class RoundImage extends Drawable { private final Bitmap mBitmap; private final Paint mPaint; private final RectF mRectF; private final int mBitmapWidth; private final int mBitmapHeight; public RoundImage(Bitmap bitmap) { mBitmap = bitmap; mRectF = new RectF(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint.setShader(shader); mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); } @Override public void draw(Canvas canvas) { canvas.drawOval(mRectF, mPaint); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mRectF.set(bounds); } @Override public void setAlpha(int alpha) { if (mPaint.getAlpha() != alpha) { mPaint.setAlpha(alpha); invalidateSelf(); } } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public int getIntrinsicWidth() { return mBitmapWidth; } @Override public int getIntrinsicHeight() { return mBitmapHeight; } public void setAntiAlias(boolean aa) { mPaint.setAntiAlias(aa); invalidateSelf(); } @Override public void setFilterBitmap(boolean filter) { mPaint.setFilterBitmap(filter); invalidateSelf(); } @Override public void setDither(boolean dither) { mPaint.setDither(dither); invalidateSelf(); } public Bitmap getBitmap() { return mBitmap; } } 

Очень хорошее решение, которое я нашел ( здесь ), делает именно то, что мне нужно, за исключением того, что оно использует все это в самом ImageView, а не для создания drawable. Это означает, что я не могу установить его, например, в качестве фона представления.

Вопрос

Как я могу это достичь?


EDIT: это текущий код, и поскольку я хотел добавить границу, у него также есть этот код для него:

 public class SimpleRoundedDrawable extends BitmapDrawable { private final Path p = new Path(); private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); public SimpleRoundedDrawable(final Resources res, final Bitmap bitmap) { super(res, bitmap); mBorderPaint.setStyle(Paint.Style.STROKE); } public SimpleRoundedDrawable setBorder(float borderWidth, @ColorInt int borderColor) { mBorderPaint.setStrokeWidth(borderWidth); mBorderPaint.setColor(borderColor); invalidateSelf(); return this; } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); p.rewind(); p.addCircle(bounds.width() / 2, bounds.height() / 2, Math.min(bounds.width(), bounds.height()) / 2, Path.Direction.CW); } @Override public void draw(Canvas canvas) { canvas.clipPath(p); super.draw(canvas); final float width = getBounds().width(), height = getBounds().height(); canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, mBorderPaint); } } 

Надеюсь, так все должно работать.


EDIT: Кажется, что решение работает только с определенной версии Android, так как оно не работает на Android 4.2.2. Вместо этого он отображает квадрат изображения.

EDIT: кажется, что вышеупомянутое решение также намного менее эффективно, чем использование BitmapShader (ссылка здесь ). Было бы очень здорово знать, как использовать его в чертеже, а не в настраиваемом ImageView

– Вот текущая модифицированная версия следующих решений. Я надеюсь, что это будет полезно для некоторых людей:

 public class SimpleRoundedDrawable extends Drawable { final Paint mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG), mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); Bitmap mBitmap; int mSide; float mRadius; public SimpleRoundedDrawable() { this(null); } public SimpleRoundedDrawable(Bitmap bitmap) { this(bitmap, 0, 0); } public SimpleRoundedDrawable(Bitmap bitmap, float width, @ColorInt int color) { mBorderPaint.setStyle(Paint.Style.STROKE); mBitmap = bitmap; mSide = mBitmap == null ? 0 : Math.min(bitmap.getWidth(), bitmap.getHeight()); mBorderPaint.setStrokeWidth(width); mBorderPaint.setColor(color); } public SimpleRoundedDrawable setBitmap(final Bitmap bitmap) { mBitmap = bitmap; mSide = Math.min(bitmap.getWidth(), bitmap.getHeight()); invalidateSelf(); return this; } public SimpleRoundedDrawable setBorder(float width, @ColorInt int color) { mBorderPaint.setStrokeWidth(width); mBorderPaint.setColor(color); invalidateSelf(); return this; } @Override protected void onBoundsChange(Rect bounds) { if (mBitmap == null) return; Matrix matrix = new Matrix(); RectF src = new RectF(0, 0, mSide, mSide); src.offset((mBitmap.getWidth() - mSide) / 2f, (mBitmap.getHeight() - mSide) / 2f); RectF dst = new RectF(bounds); final float strokeWidth = mBorderPaint.getStrokeWidth(); if (strokeWidth > 0) dst.inset(strokeWidth, strokeWidth); matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER); Shader shader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); shader.setLocalMatrix(matrix); mMaskPaint.setShader(shader); matrix.mapRect(src); mRadius = src.width() / 2f; } @Override public void draw(Canvas canvas) { Rect b = getBounds(); if (mBitmap != null) canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius, mMaskPaint); final float strokeWidth = mBorderPaint.getStrokeWidth(); if (strokeWidth > 0) canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius + strokeWidth / 2, mBorderPaint); } @Override public void setAlpha(int alpha) { mMaskPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(ColorFilter cf) { mMaskPaint.setColorFilter(cf); invalidateSelf(); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } } 

Попробуйте этот минималистский пользовательский Drawable и измените его в соответствии с вашими потребностями:

 class D extends Drawable { Bitmap bitmap; Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); int side; float radius; public D(Bitmap wrappedBitmap) { bitmap = wrappedBitmap; borderPaint.setStyle(Paint.Style.STROKE); borderPaint.setStrokeWidth(16); borderPaint.setColor(0xcc220088); side = Math.min(bitmap.getWidth(), bitmap.getHeight()); } @Override protected void onBoundsChange(Rect bounds) { Matrix matrix = new Matrix(); RectF src = new RectF(0, 0, side, side); src.offset((bitmap.getWidth() - side) / 2f, (bitmap.getHeight() - side) / 2f); RectF dst = new RectF(bounds); dst.inset(borderPaint.getStrokeWidth(), borderPaint.getStrokeWidth()); matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER); Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); shader.setLocalMatrix(matrix); maskPaint.setShader(shader); matrix.mapRect(src); radius = src.width() / 2f; } @Override public void draw(Canvas canvas) { Rect b = getBounds(); canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius, maskPaint); canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius + borderPaint.getStrokeWidth() / 2, borderPaint); } @Override public void setAlpha(int alpha) {} @Override public void setColorFilter(ColorFilter cf) {} @Override public int getOpacity() {return PixelFormat.TRANSLUCENT;} } 

Если я буду следовать за вами правильно, ваш класс Drawable будет таким:

 public class CroppedDrawable extends BitmapDrawable { private Path p = new Path(); public CroppedDrawable(Bitmap b) { super(b); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); p.rewind(); p.addCircle(bounds.width() / 2, bounds.height() / 2, Math.min(bounds.width(), bounds.height()) / 2, Path.Direction.CW); } @Override public void draw(Canvas canvas) { canvas.clipPath(p); super.draw(canvas); } } 

Пример использования:

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mila); CroppedDrawable cd = new CroppedDrawable(bitmap); imageView.setImageDrawable(cd); 

Что, с вашим предыдущим образцом образца, даст что-то вроде этого:

Скриншот

Быстрый проект CircleImageDrawable на основе моей библиотеки CircleImageView. Это не создает новый Bitmap, использует BitmapShader для достижения желаемого эффекта и центрирования изображения.

 public class CircleImageDrawable extends Drawable { private final RectF mBounds = new RectF(); private final RectF mDrawableRect = new RectF(); private final RectF mBorderRect = new RectF(); private final Matrix mShaderMatrix = new Matrix(); private final Paint mBitmapPaint = new Paint(); private final Paint mBorderPaint = new Paint(); private int mBorderColor = Color.BLACK; private int mBorderWidth = 0; private Bitmap mBitmap; private BitmapShader mBitmapShader; private int mBitmapWidth; private int mBitmapHeight; private float mDrawableRadius; private float mBorderRadius; public CircleImageDrawable(Bitmap bitmap) { mBitmap = bitmap; mBitmapHeight = mBitmap.getHeight(); mBitmapWidth = mBitmap.getWidth(); } @Override public void draw(Canvas canvas) { canvas.drawCircle(mBounds.width() / 2.0f, mBounds.height() / 2.0f, mDrawableRadius, mBitmapPaint); if (mBorderWidth != 0) { canvas.drawCircle(mBounds.width() / 2.0f, mBounds.height() / 2.0f, mBorderRadius, mBorderPaint); } } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mBounds.set(bounds); setup(); } private void setup() { mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint.setAntiAlias(true); mBitmapPaint.setShader(mBitmapShader); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setAntiAlias(true); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderRect.set(mBounds); mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f); mDrawableRect.set(mBorderRect); mDrawableRect.inset(mBorderWidth, mBorderWidth); mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f); updateShaderMatrix(); invalidateSelf(); } private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { scale = mDrawableRect.height() / (float) mBitmapHeight; dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; } else { scale = mDrawableRect.width() / (float) mBitmapWidth; dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; } mShaderMatrix.setScale(scale, scale); mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top); mBitmapShader.setLocalMatrix(mShaderMatrix); } @Override public void setAlpha(int alpha) { mBitmapPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(ColorFilter colorFilter) { mBitmapPaint.setColorFilter(colorFilter); invalidateSelf(); } @Override public int getOpacity() { return 0; } } 

Как кажется, использование «clipPath» неэффективно и требует аппаратного ускорения, отключенного в 4.3 и ниже.

Лучшее решение – использовать что-то вроде этой библиотеки, используя BitmapShader:

https://github.com/hdodenhof/CircleImageView

Который основан на:

http://www.curious-creature.com/2012/12/11/android-recipe-1-image-with-rounded-corners/

Соответствующий код:

 BitmapShader shader; shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setShader(shader); RectF rect = new RectF(0.0f, 0.0f, width, height); // rect contains the bounds of the shape // radius is the radius in pixels of the rounded corners // paint contains the shader that will texture the shape canvas.drawRoundRect(rect, radius, radius, paint); 

Я все еще хочу знать, как сделать все это в drawable, если входной файл является растровым изображением.

Существует уже встроенный способ выполнить это, и это 1 строка кода (ThumbnailUtils.extractThumbnail ())

 int dimension = getSquareCropDimensionForBitmap(bitmap); bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension); ... //I added this method because people keep asking how //to calculate the dimensions of the bitmap...see comments below public int getSquareCropDimensionForBitmap(Bitmap bitmap) { //If the bitmap is wider than it is tall //use the height as the square crop dimension if (bitmap.getWidth() >= bitmap.getHeight()) { dimension = bitmap.getHeight(); } //If the bitmap is taller than it is wide //use the width as the square crop dimension else { dimension = bitmap.getWidth(); } } 

Если вы хотите, чтобы объект растрового изображения был переработан, вы можете передать параметры, которые делают это так:

 bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT); 

Ниже приведена ссылка на документацию: ThumbnailUtils Documentation