Enable and Disable Zoom in ImageView - android

I'm using https://github.com/chrisbanes/PhotoView to enable pinch to zoom feature on my custom imageview, however, I'm having a problem with implementing the feature on a toggle button. I wanted to make the image zoom-able when the user clicked the toggle button On. After zooming, when the user clicked the zoom button off, the image should still on its zoomed position but will not be zoomable (because I have another onTouchListener where the user may click and detect colors of the image). Problem is, when the toggle button changes state (on to off), the zoom will reset. Please help me and I'm sorry for my terrible explanation. Thank you.
Below is part of my code:
case R.id.btnZoom:
PhotoViewAttacher photoView= new PhotoViewAttacher(ivImageEditor);
photoView.update();
if(btnZoom.isChecked()){
//photoView.setZoomable(true);
}else if (!btnZoom.isChecked()){
photoView.setZoomable(false);
}
break;
Updated: Below is my activity on which I put the onTouch event for the ImageView to get the pixel colors. (whereas the PhotoView library also override onTouch to enable image zooming). I tried to implement the method from this issue: Android PhotoView Keep Zoom After Orientation Change to keep the scale size/ the zoom size on different state of toggle button but I fail to understand what mScreenBase is. I hope you're able to understand what my problem is. Thanks.
ImageEditorActivity.java:
package com.example.satellite.coloridentifier;
public class ImageEditorActivity extends AppCompatActivity implements View.OnClickListener, TextToSpeech.OnInitListener {
public ImageView ivImageEditor, ivColorPicked;
public TextView tvColorName, tvHexCode, tvRGB;
public ToggleButton btnZoom;
public Bitmap myBitmap;
private float ZoomLevel;
private Matrix theMatrix;
// private RectF rectF;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_editor);
btnZoom = (ToggleButton) findViewById(R.id.btnZoom);
btnZoom.setOnClickListener(this);
ivImageEditor = (ImageView) findViewById(R.id.ivImageEditor);
ivColorPicked = (ImageView) findViewById(R.id.ivColorPicked);
tvColorName = (TextView) findViewById(R.id.tvColorName);
tvHexCode = (TextView) findViewById(R.id.tvHexCode);
tvRGB = (TextView) findViewById(R.id.tvRGB);
// get image or get photo if exist
try {
String imagePath = getIntent().getExtras().getString("imagepath");
String photo = getIntent().getExtras().getString("photoUri");
if (imagePath != null) {
myBitmap = BitmapFactory.decodeFile(imagePath);
ivImageEditor.setImageBitmap(myBitmap);
} else if (photo != null) {
Uri photoUri = Uri.parse(photo);
myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), photoUri);
ivImageEditor.setImageBitmap(myBitmap);
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "No image or photo", Toast.LENGTH_SHORT).show();
}
//get pixel colors from image on touched parts
ivImageEditor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!(ivImageEditor.getDrawable() == null)) {
int touchX = (int) motionEvent.getX();
int touchY = (int) motionEvent.getY();
ivImageEditor.buildDrawingCache();
Bitmap bitmap = ivImageEditor.getDrawingCache();
if (touchX > 0 && touchY > 0 && touchX < bitmap.getWidth() && touchY < bitmap.getHeight()) {
int pixel = bitmap.getPixel(touchX, touchY);
int red = Color.red(pixel);
int myRed = (int) Math.round(red * 100.0 / 255);
int green = Color.green(pixel);
int myGreen = (int) Math.round(green * 100.0 / 255);
int blue = Color.blue(pixel);
int myBlue = (int) Math.round(blue * 100.0 / 255);
updateColorRGB(red, green, blue);
updateColorPercentage(myRed, myGreen, myBlue);
}
}
return true;
}
});
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnZoom:
final PhotoViewAttacher photoView = new PhotoViewAttacher(ivImageEditor);
if (btnZoom.isChecked()) {
photoView.setOnMatrixChangeListener(new PhotoViewAttacher.OnMatrixChangedListener() {
#Override
public void onMatrixChanged(RectF rectF) {
theMatrix = photoView.getDisplayMatrix();
float[] theFloat = new float[9];
theMatrix.getValues( theFloat );
RectF theRect = photoView.getDisplayRect();
if (theRect != null)
{
if( theRect.left > ( ivImageEditor.getWidth() / 2 ) || ( theRect.left >= 0 ) )
{
theRect.left = 0;
}
else
{
theRect.left = ( theRect.left - ( ivImageEditor.getWidth() / 2 ) ) / mScreenBase;
}
if( theRect.top > ( ivImageEditor.getHeight() / 2 ) || ( theRect.top >= 0 ) )
{
theRect.top = 0;
}
else
{
theRect.top = ( theRect.top - ( ivImageEditor.getHeight() / 2 ) ) / mScreenBase;
}
ZoomLevel = photoView.getScale();
}
}
});
} else if (!btnZoom.isChecked()) {
photoView.setScaleType(ImageView.ScaleType.CENTER_CROP);
photoView.setDisplayMatrix(theMatrix);
photoView.setScale(ZoomLevel);
// Toast.makeText(this, "level"+ZoomLevel, Toast.LENGTH_SHORT).show();
ivImageEditor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!(ivImageEditor.getDrawable() == null)) {
int touchX = (int) motionEvent.getX();
int touchY = (int) motionEvent.getY();
ivImageEditor.buildDrawingCache();
Bitmap bitmap = ivImageEditor.getDrawingCache();
if (touchX > 0 && touchY > 0 && touchX < bitmap.getWidth() && touchY < bitmap.getHeight()) {
int pixel = bitmap.getPixel(touchX, touchY);
int red = Color.red(pixel);
int myRed = (int) Math.round(red * 100.0 / 255);
int green = Color.green(pixel);
int myGreen = (int) Math.round(green * 100.0 / 255);
int blue = Color.blue(pixel);
int myBlue = (int) Math.round(blue * 100.0 / 255);
updateColorRGB(red, green, blue);
updateColorPercentage(myRed, myGreen, myBlue);
}
}
return true;
}
});
}
break;
default:
break;
}
}
public void updateColorPercentage(int redColor, int greenColor, int blueColor) {
DbHelper myDbHelper = new DbHelper(ImageEditorActivity.this);
String colorName = myDbHelper.getColorName(redColor, greenColor, blueColor);
tvColorName.setText(colorName);
tvRGB.setText("(" + String.valueOf(redColor) + "%, " + String.valueOf(greenColor) + "%, " + String.valueOf(blueColor) + "% )");
}
public void updateColorRGB(int red, int green, int blue) {
String colorHex = String.format("#%02X%02X%02X", red, green, blue);
tvHexCode.setText(colorHex);
ivColorPicked.setBackgroundColor(Color.parseColor(colorHex));
}
}

getSuppMatrix(); allows you to save your PhotoView current state. Then disable or enable zoom and reload your PhotoView with the previous state.
Try this to disable zoom on your PhotoView :
Matrix theMatrix = new Matrix();
mPhotoView.getSuppMatrix(theMatrix);
mPhotoView.setZoomable(false);
mPhotoView.setDisplayMatrix(theMatrix);
And this to re enable zoom :
Matrix theMatrix = new Matrix();
mPhotoView.getSuppMatrix(theMatrix);
mPhotoView.setZoomable(true);
mPhotoView.setDisplayMatrix(theMatrix);

Here is the code for update() and setZoomable function from the library. As you can see they reset the drawable matrix every time the update() is called and zoomable is false. So there is no inbuilt way you can persist the state of the image by using this library.
public void setZoomable(boolean zoomable) {
mZoomEnabled = zoomable;
update();
}
public void update() {
if (mZoomEnabled) {
// Update the base matrix using the current drawable
updateBaseMatrix(mImageView.getDrawable());
} else {
// Reset the Matrix...
resetMatrix();
}
}
Here is the similar issue on GitHub Repo : Issue 168. The solution would be to store the focus point and the scale of the zoomed Image and after setting this : photoView.setZoomable(false);, reassign the focus point and the zoomed scale. This might help you implementing the solution : Android PhotoView Keep Zoom After Orientation Change.
Hope it helps!

Related

Is it possible to draw a bitmap multiple times with only 2 instances of the bitmap?

I'm trying to understand game-creation in Android. Therefore I try to program a simple TicTacToe without using buttons or layout files. Instead I want to only use bitmaps.
My Problem is that I can create the board, toggle the "X" and "O" symbol correctly, but my onDraw() only draws 2 symbols simultanously at max and I dont quite understand why or how to solve this.
public class GameScreen extends View {
List<TicTacToeSymbol> symbolList = new ArrayList<>();
float posX;
float posY;
int displayWidth;
int displayHeight;
Bitmap background;
Bitmap bitmapX;
Bitmap bitmapO;
Rect dst = new Rect();
boolean touched = false;
TicTacToeSymbol currentSymbol;
TicTacToeSymbol symbolX;
TicTacToeSymbol symbolO;
Grid grid = new Grid();
// Declaring the coordinates for the colums and rows
int ZERO_COLUMN_X = 0;
int FIRST_COLUMN_X = 480;
int SECOND_COLUMN_X = 910;
int THIRD_COLUMN_X = 1425;
(...)
int centerX = 0;
int centerY = 0;
public GameScreen(Context context) {
super(context);
try {
AssetManager assetManager = context.getAssets();
InputStream inputStream = assetManager.open("tictactoegrid.jpg");
background = BitmapFactory.decodeStream(inputStream);
inputStream = assetManager.open("X.png");
bitmapX = BitmapFactory.decodeStream(inputStream);
inputStream = assetManager.open("O.png");
bitmapO = BitmapFactory.decodeStream(inputStream);
inputStream.close();
// I create only 2 symbols, which might be the problem
symbolX = new TicTacToeSymbol(0, 0, 1);
symbolX.setBitmap(bitmapX);
symbolO = new TicTacToeSymbol(0, 0, 2);
symbolO.setBitmap(bitmapO);
setCurrentSymbol(symbolX, 1);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Display display = this.getDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x;
displayHeight = size.y;
}
public void toggleCurrentSymbol() {
if (currentSymbol == symbolX) {
currentSymbol = symbolO;
} else {
currentSymbol = symbolX;
}
}
public void setCurrentSymbol(TicTacToeSymbol ticTacToeSymbol, int type) {
this.currentSymbol = ticTacToeSymbol;
if (type == 1) {
currentSymbol.setType(1);
} else
currentSymbol.setType(2);
}
public void setCoordinatesCurrentSymbol(int centerX, int centerY) {
currentSymbol.setPosX(centerX);
currentSymbol.setPosY(centerY);
}
#Override
public void onDraw(Canvas canvas) {
dst.set(0, 0, displayWidth, displayHeight - 200);
canvas.drawBitmap(background, null, dst, null);
if (touched) {
// Checking where the user has clicked
// First row
if (posX >= ZERO_COLUMN_X && posX <= FIRST_COLUMN_X
&& posY > ZERO_ROW_Y && posY < FIRST_ROW_Y) {
centerX = FIRST_CENTER_X;
centerY = FIRST_CENTER_Y;
setCoordinatesCurrentSymbol(centerX, centerY);
grid.placeSignInGrid(0, 0, currentSymbol);
toggleCurrentSymbol();
}
if (posX > FIRST_COLUMN_X && posX <= SECOND_COLUMN_X
&& posY > ZERO_ROW_Y && posY < FIRST_ROW_Y) {
centerX = SECOND_CENTER_X;
centerY = FIRST_CENTER_Y;
setCoordinatesCurrentSymbol(centerX, centerY);
grid.placeSignInGrid(1, 0, currentSymbol);
toggleCurrentSymbol();
}
(...)
}
// Go through the grid, get the symbol at the position and add it to the list of symbols
for (int i = 0; i < Grid.GRID_SIZE; i++) {
for (int j = 0; j < Grid.GRID_SIZE; j++) {
if (grid.getSymbolAtField(i, j) != null) {
if (!symbolList.contains(grid.getSymbolAtField(i, j))) {
symbolList.add(grid.getSymbolAtField(i, j));
}
}
}
}
// Draw every symbol in the list.
for (TicTacToeSymbol ttts : symbolList) {
canvas.drawBitmap(ttts.getBitmap(), ttts.getPosX(), ttts.getPosY(), null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
touched = true;
posX = event.getX();
posY = event.getY();
invalidate();
return true;
}
return false;
}
}
I create only 2 TicTacToeSymbols, namely symbolXand symbolO. Therefore the symbolList only contains these 2 and draws only those 2.
But how can I draw the same symbol several times? Else I would have to create 9 X-Symbols including the bitmap and 9 O-Symbols to cover all grid-fields with the possible symbols. This would seem wrong / not very elegant?
So how can I draw my 2 symbols several times at the correct positions?
I've looked into several posts like this but could not derive a solution for my problem:
Draw multiple times from a single bitmap on a canvas ... this positions the bitmaps randomly, but I want my symbols on specific positions.

Pass data of touch event from custom view to activity android

How do I pass data from custom view to parent activity? I've data of which box is touched in the custom view. I want to pass that data to the parent activity whenever a box is touched.
Here's my activity layout:
1
Edit:
Here's parent activity:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible">
<SearchView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
>
</SearchView>
</RelativeLayout>
<com.example.gaurav.dummyapp.FaceOverlayView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/face_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
FaceOverlayView.java
public class FaceOverlayView extends View {
private Bitmap mBitmap;
private SparseArray<Face> mFaces;
float left = 0;
float top = 0;
float right = 0;
float bottom = 0;
double sc;
private String faceData;
public FaceOverlayView(Context context) {
this(context, null);
}
public FaceOverlayView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FaceOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setBitmap( Bitmap bitmap ) {
mBitmap = bitmap;
FaceDetector detector = new FaceDetector.Builder( getContext() )
.setTrackingEnabled(true)
.setLandmarkType(FaceDetector.ALL_LANDMARKS)
.setMode(FaceDetector.ACCURATE_MODE)
.build();
if (!detector.isOperational()) {
//Handle contingency
} else {
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
mFaces = detector.detect(frame);
detector.release();
}
logFaceData();
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if ((mBitmap != null) && (mFaces != null)) {
double scale = drawBitmap(canvas);
drawFaceBox(canvas, scale);
}
}
private double drawBitmap(Canvas canvas) {
double viewWidth = canvas.getWidth();
double viewHeight = canvas.getHeight();
double imageWidth = mBitmap.getWidth();
double imageHeight = mBitmap.getHeight();
double scale = Math.min(viewWidth / imageWidth, viewHeight / imageHeight);
sc = scale;
Rect destBounds = new Rect(0, 0, (int)(imageWidth * scale), (int)(imageHeight * scale));
canvas.drawBitmap(mBitmap, null, destBounds, null);
return scale;
}
private void drawFaceBox(Canvas canvas, double scale) {
//This should be defined as a member variable rather than
//being created on each onDraw request, but left here for
//emphasis.
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
left = (float) ( face.getPosition().x * scale );
top = (float) ( face.getPosition().y * scale );
right = (float) scale * ( face.getPosition().x + face.getWidth() );
bottom = (float) scale * ( face.getPosition().y + face.getHeight() );
canvas.drawRect( left, top, right, bottom, paint );
}
}
private void drawFaceLandmarks(Canvas canvas, double scale ) {
Paint paint = new Paint();
paint.setColor( Color.GREEN );
paint.setStyle( Paint.Style.STROKE );
paint.setStrokeWidth( 5 );
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
for ( Landmark landmark : face.getLandmarks() ) {
int cx = (int) ( landmark.getPosition().x * scale );
int cy = (int) ( landmark.getPosition().y * scale );
canvas.drawCircle( cx, cy, 10, paint );
}
}
}
private void logFaceData() {
float smilingProbability;
float leftEyeOpenProbability;
float rightEyeOpenProbability;
float eulerY;
float eulerZ;
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
smilingProbability = face.getIsSmilingProbability();
leftEyeOpenProbability = face.getIsLeftEyeOpenProbability();
rightEyeOpenProbability = face.getIsRightEyeOpenProbability();
eulerY = face.getEulerY();
eulerZ = face.getEulerZ();
Log.e( "Face Detection", "Smiling: " + smilingProbability );
Log.e( "Face Detection", "Left eye open: " + leftEyeOpenProbability );
Log.e( "Face Detection", "Right eye open: " + rightEyeOpenProbability );
Log.e( "Face Detection", "Euler Y: " + eulerY );
Log.e( "Face Detection", "Euler Z: " + eulerZ );
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int x = (int) event.getX();
int y = (int) event.getY();
float eulerY;
float eulerZ;
switch(action) {
case MotionEvent.ACTION_DOWN : {
for( int i = 0; i < mFaces.size(); i++ ) {
Face face = mFaces.valueAt(i);
left = (float) ( face.getPosition().x * sc );
top = (float) ( face.getPosition().y * sc );
right = (float) sc * ( face.getPosition().x + face.getWidth() );
bottom = (float) sc * ( face.getPosition().y + face.getHeight() );
if((x > left && x < right) && (y > top && y < bottom)){
Log.e("BOX>>>>>>>>>>", String.valueOf(i));
}
}
break;
}
case MotionEvent.ACTION_MOVE : {
//path.lineTo(event.getX(), event.getY());
break;
}
}
invalidate();
return super.onTouchEvent(event);
}
Activity Class:
public class dummyActivity extends AppCompatActivity {
private FaceOverlayView fooView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dummy);
fooView.setBitmap("bitmap");
}
i think what you need is onTouchListenter on the parent activity
box_view.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
// Do what you want/Get data from view etc... depends what kind of data it is, can you explain more?
return true;
}
return false;
}
});
i think you have a textfield inside the box that hold the integer value, so you do inside the onTouch
tv_box_1.getText();
//To get the text of the box1 (do for tv_box_2.getText) etc...
//after that just convert to integer
Integer.parseInt(tv_box_1.getText());
you can do another way, implementing the touch inside the parent and handle the touch, doing some ifs to check the View id and handle that
another way and easier is to give the TextView inside the box the match_parent for both direction and set the onClick, its the easier, you should use this one if you dont really need the touch

How to add multiple pin marker on image dyanamically

I want to put marker on image with below functionality
-Dynamically adding multiple markers on image with zoom and scroll functionality on image.
-Blink animation on marker after added on image.
-Marker can be click-able and drag & drop on image.
-Marker's x & y position on image to sync same with other devices
I have used https://github.com/davemorrissey/subsampling-scale-image-view library
Java Code :
public class PinMarkerView extends SubsamplingScaleImageView implements View.OnTouchListener {
private PointF sPin;
private Bitmap pin;
private int resId;
public float vX;
public float vY;
private PointF vPrevious;
private PointF vStart;
private boolean drawing = false;
private boolean markerTouch = false;
private int strokeWidth;
private ArrayList<MapPins> allPins = new ArrayList<MapPins>();
private ArrayList<DrawPins> drawnPins = new ArrayList<DrawPins>();
public PinView(Context context) {
this(context, null);
}
public PinView(Context context, AttributeSet attr) {
super(context, attr);
// initialise();
}
public void setPin(PointF sPin, #DrawableRes int resid, String extras) {
MapPins mapPins = new MapPins();
mapPins.setPointF(sPin);
mapPins.setResId(resid);
mapPins.setExtrasData(extras);
mapPins.setX(sPin.x);
mapPins.setY(sPin.y);
allPins.add(mapPins);
//this.sPin = sPin;
// this.resId = resid;
initialise();
invalidate();
}
public PointF getPin() {
return sPin;
}
public Bitmap getMarker() {
return pin;
}
private void initialise() {
setOnTouchListener(this);
float density = getResources().getDisplayMetrics().densityDpi;
strokeWidth = (int) (density / 60f);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
Bitmap localpin = BitmapFactory.decodeResource(this.getResources(), mapPins.getResId());
float w = (density / 100f) * localpin.getWidth();
float h = (density / 100f) * localpin.getHeight();
//pin = Bitmap.createScaledBitmap(pin, (int) w, (int) h, true);
pin = Bitmap.createScaledBitmap(localpin, localpin.getWidth(), localpin.getHeight(), true);
mapPins.setCreatedBitmap(pin);
allPins.set(i, mapPins);
//-------
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw pin before image is ready so it doesn't move around during setup.
if (!isReady()) {
return;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
if (mapPins.getPointF() != null && mapPins.getCreatedBitmap() != null) {
PointF vPin = sourceToViewCoord(mapPins.getPointF());
vX = vPin.x - (mapPins.getCreatedBitmap().getWidth() / 2);
vY = vPin.y - mapPins.getCreatedBitmap().getHeight();
canvas.drawBitmap(mapPins.getCreatedBitmap(), vX, vY, paint);
}
}
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
setMaxScale(5f);
return false;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
/* if (isZoomEnabled()) {
return super.onTouchEvent(event);
}*/
boolean consumed = false;
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
vStart = new PointF(event.getX(), event.getY());
vPrevious = new PointF(event.getX(), event.getY());
getPinIdByPoint(vStart, viewToSourceCoord(event.getX(), event.getY()));
handleActionDown((int) event.getX(), (int) event.getY());
break;
case MotionEvent.ACTION_POINTER_2_DOWN:
// Abort any current drawing, user is zooming
vStart = null;
vPrevious = null;
break;
case MotionEvent.ACTION_MOVE:
if (markerTouch) {
setPanEnabled(false);
PointF sCurrentF = viewToSourceCoord(event.getX(), event.getY());
PointF sCurrent = new PointF(sCurrentF.x, sCurrentF.y);
PointF sStart = vStart == null ? null : new PointF(viewToSourceCoord(vStart).x, viewToSourceCoord(vStart).y);
if (touchCount == 1 && vStart != null) {
float vDX = Math.abs(event.getX() - vPrevious.x);
float vDY = Math.abs(event.getY() - vPrevious.y);
if (vDX >= strokeWidth * 5 || vDY >= strokeWidth * 5) {
if (sPin == null) {
sPin = sStart;
}
sPin = sCurrent;
vPrevious.x = event.getX();
vPrevious.y = event.getY();
drawing = true;
}
consumed = true;
invalidate();
} else if (touchCount == 1) {
// Consume all one touch drags to prevent odd panning effects handled by the superclass.
consumed = true;
}
} else {
return super.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
invalidate();
setPanEnabled(true);
drawing = false;
markerTouch = false;
vPrevious = null;
vStart = null;
}
// Use parent to handle pinch and two-finger pan.
return consumed || super.onTouchEvent(event);
}
public void handleActionDown(int eventX, int eventY) {
if (eventX >= (sPin.x - pin.getWidth()) && (eventX <= (sPin.x + pin.getWidth()))) {
if (eventY >= (sPin.y - pin.getHeight()) && (sPin.y <= (sPin.y + pin.getHeight()))) {
markerTouch = true;
} else {
markerTouch = false;
}
} else {
markerTouch = false;
}
}
public int getPinIdByPoint(PointF tappedCoordinate, PointF deeplinkCoordinate) {
for (int i = allPins.size() - 1; i >= 0; i--) {
MapPins dPin = allPins.get(i);
int blockWidth = dPin.getCreatedBitmap().getWidth();
int blockHeight = dPin.getCreatedBitmap().getHeight();
int deeplinkX = (int) (deeplinkCoordinate.x - (dPin.getCreatedBitmap().getWidth() / 2));
int deeplinkY = (int) (deeplinkCoordinate.y - dPin.getCreatedBitmap().getHeight());
// center coordinate -/+ blockWidth actually sets touchable area to 2x icon size
if (tappedCoordinate.x >= deeplinkX - blockWidth && tappedCoordinate.x <= deeplinkX + blockWidth &&
tappedCoordinate.y >= deeplinkY - blockHeight && tappedCoordinate.y <= deeplinkY + blockHeight) {
sPin = dPin.getPointF();
pin = dPin.getCreatedBitmap();
return dPin.getId();
}
}
return -1; //negative no means no pin selected
}
}
Currently, I am able to add multiple marker, but I am facing issue of marker click and dragging on image.

Custom shaped buttons in Android - Am I doing it right?

I'm trying to implement four custom shaped buttons like you can see in the following picture:
What I did so far: I took 4 different pictures - each with only one color visible (see above). The other part of the image is transparent. This has the result, that I have four pictures with the same size.
Now I used a relative-layout where all my 4 pictures are added into imageviews on the same position. Because of the transparency, I can see the desired picture.
For my ImageViews I've implemented onTouchListener with the following content:
private class ImageOnTouchListener implements View.OnTouchListener {
private int categoryId;
public ImageOnTouchListener(int categoryId) {
this.categoryId = categoryId;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
int x = (int) event.getX();
int y = (int) event.getY();
boolean isInsideBitmap = x < bmp.getWidth() && y < bmp.getHeight() && x >= 0 && y >= 0;
boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;
if (isInsideBitmap) {
int color = bmp.getPixel(x, y);
bmp.recycle();
if (color == Color.TRANSPARENT){
return false;
}
else {
if (isActionUp) {
buttonClick();
}
}
}else{
bmp.recycle();
}
return true;
}
}
This approach works but it consumes a lot of memory as I'm always creating a bitmap when I move my finger. I'm not quite sure if this is the best way to implement this. Is there anything I can do different which might result in a more efficient way?
Using the fact that you can tell whether or not coordinates belong in a circle with the equation x² + y² <= radius² when the circle's center is (0, 0), I think the following should work.
public class ImageOnTouchListener implements View.OnTouchListener {
// TODO Adjust this value
private static int QUADRANT_RADIUS = 100; // in pixels
// TODO Adjust this value
private static int SPACE_BETWEEN_QUADRANTS = 5; // in pixels
#Override
public boolean onTouch(View v, MotionEvent event) {
int relativeX = (int) (event.getX() - v.getX());
int relativeY = (int) (event.getY() - v.getY());
int center = QUADRANT_RADIUS + (SPACE_BETWEEN_QUADRANTS / 2);
boolean isInsideCircle = Math.pow(relativeX - center, 2) + Math.pow(relativeY - center, 2) <= Math.pow(center, 2);
boolean isInsideBottomLeftQuadrant = isInsideCircle &&
relativeX <= QUADRANT_RADIUS &&
relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
boolean isInsideBottomRightQuadrant = isInsideCircle &&
relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
boolean isInsideTopLeftQuadrant = isInsideCircle &&
relativeX <= QUADRANT_RADIUS &&
relativeY <= QUADRANT_RADIUS;
boolean isInsideTopRightQuadrant = isInsideCircle &&
relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
relativeY <= QUADRANT_RADIUS;
boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;
if (isActionUp) {
if (isInsideBottomLeftQuadrant) {
// Handle bottom left quadrant click
buttonClick();
return true;
} else if (isInsideBottomRightQuadrant) {
// Handle bottom right quadrant click
} // etc.
}
return false;
}
}
You'll need to adjust the QUADRANT_RADIUS and SPACE_BETWEEN_QUADRANTS values, and then either handle all touch events from a single view (like my snippet does) or have a slightly different touch listener per image.

Android scroll through in-app images in a gallery style way

I have a GridView of images, which are all in the /res directory (come with the app). When I open one of them, there is no problem. But, I want to be able to scroll between them also in the same way that's done in the gallery, i.e. sliding the finger on the image will cause the next image to appear. Is there any way to do that?
Thanks!
You can use ViewPager
See android viewPager implementation for some good links to learn how to implement it.
I ended up creating a simple implementation of my own:
public class PicView extends View{
private int mBackgroundPicPosition;
private Bitmap mBackgroundPic;
private int m_touchStartPosX;
private EventsActivity m_mainActivity;
private int m_viewWidth;
private int m_viewHeight;
private int m_backgroundX;
private Integer[] m_picIDs;
public PicView(Context context, Integer[] picIDs) {
super(context);
m_mainActivity = (EventsActivity)context;
m_viewWidth = m_mainActivity.mMetrics.widthPixels;
m_viewHeight = m_mainActivity.mMetrics.heightPixels;
m_picIDs = picIDs;
}
public void setBackground(int position) {
Options opts = new Options();
mBackgroundPicPosition = position;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), m_picIDs[position], opts);
mBackgroundPic = BitmapFactory.decodeResource(getResources(), m_picIDs[position], opts);
int picHeight = bitmap.getHeight();
int picWidth = bitmap.getWidth();
mBackgroundPic.recycle();
float xScale, yScale, scale;
if (picWidth > picHeight) {
// rotate the picture
Matrix matrix = new Matrix();
matrix.postRotate(-90);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
picHeight = bitmap.getHeight();
picWidth = bitmap.getWidth();
matrix = null;
}
xScale = ((float)m_viewWidth)/ picWidth;
yScale = ((float)m_viewHeight) / picHeight;
scale = (xScale <= yScale) ? xScale : yScale;
m_backgroundX = (xScale >= yScale) ? (m_viewWidth - (int)(picWidth * scale)) / 2 : 0;
mBackgroundPic = Bitmap.createScaledBitmap(bitmap, (int)(picWidth * scale), (int)(picHeight * scale), true);
bitmap = null;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
// draw the background
canvas.drawBitmap(mBackgroundPic, m_backgroundX, 0, null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
int X = (int)event.getX();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN:
m_touchStartPosX = X;
break;
case MotionEvent.ACTION_UP:
//check to see if sliding picture
if (X <= m_touchStartPosX) {
// slide to the left
setBackground(getNextPosition());
} else {
// slide to the right
setBackground(getPrevPosition());
}
m_touchStartPosX = -1;
break;
}
invalidate();
return true;
}
private int getPrevPosition() {
if (mBackgroundPicPosition == 0) {
return m_picIDs.length - 1;
} else {
return mBackgroundPicPosition - 1;
}
}
private int getNextPosition() {
if (mBackgroundPicPosition == m_picIDs.length - 1) {
return 0;
} else {
return mBackgroundPicPosition + 1;
}
}

Categories

Resources