Android how to apply mask on ImageView?
I have found the perfect combination for creating masking without black border after researching through all the stackoverflow posts. It suits my purpose quite well.
Currently I'm creating a draggable view using one normal image and a masking image (a png with transparency), so I'll need to override the onDraw function.
private Bitmap mImage = ...;
private Bitmap mMask = ...; // png mask with transparency
private int mPosX = 0;
private int mPosY = 0;
private final Paint maskPaint;
private final Paint imagePaint;
public CustomView (final Context context) {
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
imagePaint = new Paint();
imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}
/* TODO
if you have more constructors, make sure you initialize maskPaint and imagePaint
Declaring these as final means that all your constructors have to initialize them.
Failure to do so = your code won't compile.
*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.drawBitmap(mMask, 0, 0, maskPaint);
canvas.drawBitmap(mImage, mPosX, mPosY, imagePaint);
canvas.restore();
}
Mask ImageView with round corner background
I suggest you to use another method:
One FrameLayout
and two ImageView
can do it.
<FrameLayout>
<ImageView /> your image
<ImageView /> put a image which has a transparent circle in it
</FrameLayout>
then your image can been seen via transparent circle.
Apply custom mask to an ImageView
Thanks for the answers, which will definitely be helpful for other users.
Unfortunetly I had to help myself to get the desired results.
This is my final solution which applies a quad bezier to the ImageView.
class HeaderImageView : AppCompatImageView {
constructor(context: Context?) : super(context) {
init()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
lateinit var paint: Paint
private fun init() {
paint = Paint()
paint.color = Color.WHITE
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)
paint.style = Paint.Style.FILL
}
@SuppressLint("CanvasSize")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val fHeight = canvas.height.toFloat()
val startEndHeight = canvas.height / 1.18f
val fWidth = canvas.width.toFloat()
val halfWidth = (fWidth / 2)
val path = Path()
//X = Left side, Y = close to bottom
val ptStart = PointF(0f, startEndHeight)
//X = Middle, Y = Bottom
val ptMiddle = PointF(halfWidth, fHeight + 95)
// X = Right Side, Y = close to bottom
val ptEnd = PointF(fWidth, startEndHeight)
path.moveTo(ptStart.x, ptStart.y)
path.quadTo(ptMiddle.x, ptMiddle.y, ptEnd.x, ptEnd.y)
path.lineTo(fWidth, fHeight)
path.lineTo(0f, fHeight)
path.close()
canvas.drawPath(path, paint)
}
}
Masking(crop) image in frame
Finally got the solution while changing mask image and using of Xfermode
with Bitmap
Mask
ImageView mImageView= (ImageView)findViewById(R.id.imageview_id);
Bitmap original = BitmapFactory.decodeResource(getResources(),R.drawable.content_image);
Bitmap mask = BitmapFactory.decodeResource(getResources(),R.drawable.mask);
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(original, 0, 0, null);
mCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
mImageView.setImageBitmap(result);
mImageView.setScaleType(ScaleType.CENTER);
mImageView.setBackgroundResource(R.drawable.background_frame);
see output
Source can be found here
How to mask/crop a rectangle in ImageView
I wrote a simple View
which extends ImageView
which does what you're trying to do. To work with it, all you need to do is give it a Rect which defines the zone you want as grayscale.
Here's the code:
public class MaskImageView extends ImageView
{
private Rect mMaskRect;
public MaskImageView(Context context)
{
super(context);
init();
}
public MaskImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public MaskImageView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
mMaskRect = new Rect();
}
// Use this to define the area which should be masked.
public void setRect(Rect rect)
{
mMaskRect.set(rect);
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.clipRect(mMaskRect, Region.Op.DIFFERENCE);
super.onDraw(canvas);
}
}
I've tested it and it seems to work well. This should get you started.
Create a drag and clear mask on android image view
Here's a code sample. The important parts are 1) it uses a customized view that overlaps the image view; 2) it uses a bitmap canvas for the actual drawing (required in order to be able to clear pixels); and 3) the painting uses the special CLEAR
transfer mode.
Output:
DrawView.java:
package com.example.drawingwithmask;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class DrawView extends View implements OnTouchListener {
private Paint bmPaint = new Paint();
private Paint drawPaint = new Paint();
private Path path = new Path();
private Canvas cv = null;
private Bitmap bm = null;
private boolean firstTimeThru = true;
public DrawView(Context context) {
super(context);
init();
}
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void init() {
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
}
@Override
public void onDraw(Canvas canvas) {
// Set everything up the first time anything gets drawn:
if (firstTimeThru) {
firstTimeThru = false;
// Just quickly fill the view with a black mask:
canvas.drawColor(Color.BLACK);
// Create a new bitmap and canvas and fill it with a black mask:
bm = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
cv = new Canvas();
cv.setBitmap(bm);
cv.drawColor(Color.BLACK);
// Specify that painting will be with fat strokes:
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeWidth(canvas.getWidth() / 15);
// Specify that painting will clear the pixels instead of paining new ones:
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
cv.drawPath(path, drawPaint);
canvas.drawBitmap(bm, 0, 0, bmPaint);
super.onDraw(canvas);
}
public boolean onTouch(View view, MotionEvent event) {
float xPos = event.getX();
float yPos = event.getY();
switch (event.getAction()) {
// Set the starting position of a new line:
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos, yPos);
return true;
// Draw a line to the ending position:
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos, yPos);
break;
default:
return false;
}
// Call onDraw() to redraw the whole view:
invalidate();
return true;}
}
activity_layout:
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher" />
<com.example.drawingwithmask.DrawView
android:id="@+id/draw"
android:layout_width="match_parent"
android:layout_height="match_parent" />
MainActivity.java:
package com.example.drawingwithmask;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
DrawView drawView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
drawView = (DrawView) findViewById(R.id.draw);
}
}
Addendum:
Here's a method that can estimate if a certain threshold of mask clearing has been reached. I tested it by calling checkProgress()
from the MOTION_MOVE
event case. This might not be the best way or perhaps even a good way, but it does at least provide an estimate at the expense of STEPS^2 (400 as presented) pixel checks.
final int STEPS = 20;
final double THRESHOLD = 0.70;
private void checkProgress() {
int sum = 0;
for (int x = 0; x < bm.getWidth(); x += bm.getWidth() / STEPS)
for (int y = 0; y < bm.getHeight(); y += bm.getHeight() / STEPS)
if (bm.getPixel(x, y) != Color.BLACK)
sum++;
double progress = (double) sum / (STEPS * STEPS);
Log.v(TAG, "Cleared: " + progress);
if (progress >= THRESHOLD)
Log.i(TAG, "Done!");
}
Related Topics
How to Call Wi-Fi Settings Screen from My Application Using Android
How to Add Items to a Spinner in Android
Encryption Error on Android 4.2
How to Check If Url Is Valid in Android
Movecamera with Cameraupdatefactory.Newlatlngbounds Crashes
How to Style the Drawerarrowtoggle from Android Appcompat V7 21 Library
Getting Access to Media Player Cache
Android Runtimeexception: Unable to Instantiate the Service
How to Detect When a User Plugs Headset on Android Device? (Opposite of Action_Audio_Becoming_Noisy)
Android: Cursoradapter, Listview and Checkbox
Android SQLite How to Check If a Record Exists
Source of Android's Lock Screen
How to Set Textinputlayout Error Message Colour
Why Use Android Picasso Library to Download Images
Uploading a Large File in Multipart Using Okhttp
Android: How to Remove Margin/Padding in Preference Screen
How to Dismiss a Progress Bar Even If There Is No View to Populate in the Firebaselistadapter