I am currently in need of making ChatHeads like Facebook. Following the tutorials I found, I can make a ChatHead start from Service. However, when I added Rebound API from Facebook to make it smooth, I created a customized view like this post: Adding natural dragging effect to ImageView same as Facebook Messanger chat heads using Rebound library ;
but it seems to block every background touch. I can only drag the ChatHead around without able to touch anything else. How can I make a ChatHead that is still smooth draggable and also can interact with the background also (just the same as Facebook)? Any help is appreciated.
Thanks in advanced
BTW, this is my view
class ChatHeadView extends View implements SpringListener, SpringSystemListener {
private Spring xSpring;
private Spring ySpring;
private SpringSystem springSystem;
private final SpringConfig COASTING;
private float x;
private float y;
private boolean dragging;
private float radius = 100;
private float downX;
private float downY;
private float lastX;
private float lastY;
private VelocityTracker velocityTracker;
private float centerX;
private float centerY;
int screenWidth, screenHeight;
private Paint mPaint = new Paint();
private Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
public ChatHeadView(Context context, int width, int height) {
super(context);
springSystem = SpringSystem.create();
springSystem.addListener(this);
COASTING = SpringConfig.fromOrigamiTensionAndFriction(0, 0.5);
COASTING.tension = 0;
xSpring = springSystem.createSpring();
ySpring = springSystem.createSpring();
xSpring.addListener(this);
ySpring.addListener(this);
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
centerX = getWidth() / 2f;
centerY = getHeight() / 2f;
xSpring.setCurrentValue(centerX).setAtRest();
ySpring.setCurrentValue(centerY).setAtRest();
getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
screenWidth = width;
screenHeight = height;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, x - mBitmap.getWidth() / 2,
y - mBitmap.getHeight(), mPaint);
}
#SuppressLint("Recycle")
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getRawX();
float touchY = event.getRawY();
boolean ret = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = touchX;
downY = touchY;
lastX = downX;
lastY = downY;
velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
if (downX > x - radius && downX < x + radius && downY > y - radius
&& downY < y + radius) {
dragging = true;
ret = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (!dragging) {
break;
}
velocityTracker.addMovement(event);
float offsetX = lastX - touchX;
float offsetY = lastY - touchY;
xSpring.setCurrentValue(xSpring.getCurrentValue() - offsetX)
.setAtRest();
ySpring.setCurrentValue(ySpring.getCurrentValue() - offsetY)
.setAtRest();
checkConstraints();
ret = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (!dragging) {
break;
}
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
dragging = false;
ySpring.setSpringConfig(COASTING);
xSpring.setSpringConfig(COASTING);
downX = 0;
downY = 0;
xSpring.setVelocity(velocityTracker.getXVelocity());
ySpring.setVelocity(velocityTracker.getYVelocity());
ret = true;
}
lastX = touchX;
lastY = touchY;
return ret;
}
#Override
public void onSpringUpdate(Spring s) {
x = (float) xSpring.getCurrentValue();
y = (float) ySpring.getCurrentValue();
invalidate();
}
#Override
public void onSpringActivate(Spring s) {
}
#Override
public void onSpringAtRest(Spring s) {
}
#Override
public void onSpringEndStateChange(Spring s) {
}
#Override
public void onBeforeIntegrate(BaseSpringSystem springSystem) {
}
#Override
public void onAfterIntegrate(BaseSpringSystem springSystem) {
checkConstraints();
}
private void checkConstraints() {
if (x + radius >= screenWidth) {
xSpring.setVelocity(-xSpring.getVelocity());
xSpring.setCurrentValue(xSpring.getCurrentValue()
- (x + radius - screenWidth), false);
}
if (x - radius <= 0) {
xSpring.setVelocity(-xSpring.getVelocity());
xSpring.setCurrentValue(xSpring.getCurrentValue() - (x - radius),
false);
}
if (y + radius >= screenHeight) {
ySpring.setVelocity(-ySpring.getVelocity());
ySpring.setCurrentValue(ySpring.getCurrentValue()
- (y + radius - screenHeight), false);
}
if (y - radius <= 0) {
ySpring.setVelocity(-ySpring.getVelocity());
ySpring.setCurrentValue(ySpring.getCurrentValue() - (y - radius),
false);
}
}
}
this is where I add View with WindowManager:
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenWidth = size.x;
screenHeight = size.y;
chatHeadView = new ChatHeadView(this, screenWidth, screenHeight);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT);
windowManager.addView(chatHeadView, params);
In the documentation WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Window flag: even when this window is focusable (its FLAG_NOT_FOCUSABLE is not set), allow any pointer events outside of the window to be sent to the windows behind it. Otherwise it will consume all pointer events itself, regardless of whether they are inside of the window.
It's saying about your problem.
Instead, try WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE flag.
you can get TouchEvent on the view and it won't consume other events.
Related
So, I have ScrollView, HorizantalScrollView and BoardView for TicTacToe game.
When user zooms, while i redraw scale into categories of cells through ScalegestureDetector zoom pinch is allocated on top, left of the screen, not at the centre of pinches, how can I allocate it to the center?
here is my project in github: https://github.com/boyfox/TestTicTac.git
Project used com.android.support:appcompat-v7
Anybody have a solution to this problem?
I think that you will want to move to an implementation closer to the Android Dragging and Scaling examples (and to the similar question here).
This is the start of what you need. You can pan the view as before and now scale the view at the center of the pinch gesture. It is your base BoardView with logic to draw points on clicks from here. It should be pretty easy to draw your custom X and O icons instead of circles.
BoardView.java
public class BoardView extends View {
private static final int JUST_SCALED_DURATION = 100;
private static final int MAX_TOUCH_DURATION = 1000;
private static final int MAX_TOUCH_DISTANCE = 10;
private boolean stayedWithinTouchDistance;
private float firstTouchX, firstTouchY;
private long firstTouchTime, lastScaleTime;
private float posX, posY, lastTouchX, lastTouchY;
private ScaleGestureDetector scaleDetector;
private float minScaleFactor = 0.1f;
private float maxScaleFactor = 5.0f;
private float scaleFactor = 1.0f;
private float cellSize = 50.0f;
private int numCells = 50;
private ArrayList<Point> points;
private Paint linePaint, pointPaint;
public BoardView(Context context) {
this(context, null, 0);
}
public BoardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BoardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
points = new ArrayList<>();
linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setColor(-65536);
linePaint.setStrokeWidth(1.0f);
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setColor(-65536);
pointPaint.setStrokeWidth(1.0f);
posX = linePaint.getStrokeWidth();
posY = linePaint.getStrokeWidth();
scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
private static float distancePx(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
return (float) Math.sqrt(dx * dx + dy * dy);
}
private float distanceDp(float distancePx) {
return distancePx / getResources().getDisplayMetrics().density;
}
private Point coerceToGrid(Point point) {
point.x = (int) ((((int) (point.x / cellSize)) * cellSize) + (cellSize / 2));
point.y = (int) ((((int) (point.y / cellSize)) * cellSize) + (cellSize / 2));
return point;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
scaleDetector.onTouchEvent(event);
final int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_DOWN) {
stayedWithinTouchDistance = true;
firstTouchX = lastTouchX = event.getRawX();
firstTouchY = lastTouchY = event.getRawY();
firstTouchTime = System.currentTimeMillis();
} else if (action == MotionEvent.ACTION_MOVE) {
float thisTouchX = event.getRawX();
float thisTouchY = event.getRawY();
boolean justScaled = System.currentTimeMillis() - lastScaleTime < JUST_SCALED_DURATION;
float distancePx = distancePx(firstTouchX, firstTouchY, thisTouchX, thisTouchY);
stayedWithinTouchDistance = stayedWithinTouchDistance &&
distanceDp(distancePx) < MAX_TOUCH_DISTANCE;
if (!stayedWithinTouchDistance && !scaleDetector.isInProgress() && !justScaled) {
posX += thisTouchX - lastTouchX;
posY += thisTouchY - lastTouchY;
invalidate();
}
lastTouchX = thisTouchX;
lastTouchY = thisTouchY;
} else if (action == MotionEvent.ACTION_UP) {
long touchDuration = System.currentTimeMillis() - firstTouchTime;
if (touchDuration < MAX_TOUCH_DURATION && stayedWithinTouchDistance) {
int[] location = {0, 0};
getLocationOnScreen(location);
float x = ((lastTouchX - posX - location[0]) / scaleFactor);
float y = ((lastTouchY - posY - location[1]) / scaleFactor);
points.add(coerceToGrid(new Point((int) x, (int) y)));
invalidate();
}
}
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
lastScaleTime = System.currentTimeMillis();
float scale = detector.getScaleFactor();
scaleFactor = Math.max(minScaleFactor, Math.min(scaleFactor * scale, maxScaleFactor));
if (scaleFactor > minScaleFactor && scaleFactor < maxScaleFactor) {
float centerX = detector.getFocusX();
float centerY = detector.getFocusY();
float diffX = centerX - posX;
float diffY = centerY - posY;
diffX = diffX * scale - diffX;
diffY = diffY * scale - diffY;
posX -= diffX;
posY -= diffY;
invalidate();
return true;
}
return false;
}
}
private float getScaledCellSize() {
return scaleFactor * cellSize;
}
private float getScaledBoardSize() {
return numCells * getScaledCellSize();
}
private void drawBoard(Canvas canvas) {
for (int i = 0; i <= numCells; i++) {
float total = getScaledBoardSize();
float offset = getScaledCellSize() * i;
canvas.drawLine(offset, 0, offset, total, linePaint);
canvas.drawLine(0, offset, total, offset, linePaint);
}
}
private void drawPoints(Canvas canvas) {
for (Point point : points) {
float x = point.x * scaleFactor;
float y = point.y * scaleFactor;
float r = getScaledCellSize() / 4;
canvas.drawCircle(x, y, r, pointPaint);
}
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
float total = getScaledBoardSize();
float edge = linePaint.getStrokeWidth();
posX = Math.max(Math.min(edge, getWidth() - total - edge), Math.min(edge, posX));
posY = Math.max(Math.min(edge, getHeight() - total - edge), Math.min(edge, posY));
canvas.save();
canvas.translate(posX, posY);
drawBoard(canvas);
drawPoints(canvas);
canvas.restore();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.client.BoardView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
I want to move and scale the view on the right while dragging it to left side.I tried to set the layout parameters of this view on touch.It moved and scaled the view.But rendering is not correct when moving our finger fastly to both left and right sides.
This view on the right side is a custom layout extends LinearLayout having a ListView as child.And the left side is also another layout and integrated both layouts into a Framelayout(similar to slidingmenu).
Is there any way to render the layout (move and scale) the view without updating LayoutParams?
Is it possible to update the layout using canvas and matrix?
Here is the code for custom layout for view on the right side(the small view).
public class SlidingLayout extends LinearLayout {
private static String LOG_TAG = "SlidingLayout";
private boolean isTranformed = false;
private PanGestureListener gestureListener;
private GestureDetector gestureDetector;
private boolean isAnimating = false;
private boolean isScrolling = false;
private DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
private Matrix matrix = new Matrix();
private float posX = 0;
private float posY = 0;
public CustomSlidingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
gestureListener = new PanGestureListener();
gestureDetector = new GestureDetector(context, gestureListener);
matrix.setTranslate(0, 0);
matrix.setScale(1.0f, 1.0f);
}
#Override
protected void onDraw(Canvas canvas) {
//canvas.save();
/*canvas.drawColor(Color.RED);
canvas.translate(posX, posY);
super.onDraw(canvas);*/
/*canvas.restore();
matrix.reset();
matrix = canvas.getMatrix();*/
/*if (isTranformed) {
matrix.postTranslate(posX, posY);
canvas.setMatrix(matrix);
}
super.onDraw(canvas);*/
}
private void makeViewSmall() {
if (!isAnimating) {
isAnimating = true;
Rect rect = new Rect();
getLocalVisibleRect(rect);
ResizeMoveAnimation anim = new ResizeMoveAnimation(this,
(int) (displayMetrics.widthPixels * 0.8), displayMetrics.heightPixels / 4,
displayMetrics.widthPixels * 2, rect.bottom - displayMetrics.heightPixels
/ 4);
anim.setAnimationListener(animationListener);
anim.setDuration(1000);
anim.setInterpolator(new BounceInterpolator());
startAnimation(anim);
}
}
public void makeViewOriginal() {
if(isTranformed){
if (!isAnimating) {
isAnimating = true;
ResizeMoveAnimation anim = new ResizeMoveAnimation(this, 0, 0,
displayMetrics.widthPixels, displayMetrics.heightPixels);
anim.setAnimationListener(animationListener);
anim.setInterpolator(new BounceInterpolator());
anim.setDuration(1000);
startAnimation(anim);
}
}else{
makeViewSmall();
}
}
private AnimationListener animationListener = new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) CustomSlidingLayout.this .getLayoutParams();
if (isTranformed) {
isTranformed = false;
params.leftMargin = 0;
params.topMargin = 0;
params.width = displayMetrics.widthPixels;
params.height = displayMetrics.heightPixels;
requestLayout();
} else {
isTranformed = true;
}
isAnimating = false;
}
};
class PanGestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
if (isTranformed) {
makeViewOriginal();
return true;
}
return false;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isTranformed) {
return true;
} else {
return false;
}
}
private int _xDelta = 0;
private int _yDelta = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
//gestureDetector.onTouchEvent(event);
if (isTranformed) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
this.requestLayout();
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) this .getLayoutParams();
if (layoutParams.leftMargin == 0 && layoutParams.topMargin == 0) {
//this.requestLayout();
isTranformed = false;
isScrolling = false;
break;
}
isScrolling = true;
int xDiff = layoutParams.leftMargin - (X - _xDelta);
layoutParams.leftMargin = X - _xDelta;
int scaleFactor = layoutParams.leftMargin > 0 ? layoutParams.leftMargin : 1;
layoutParams.topMargin = layoutParams.topMargin - ((layoutParams.topMargin / scaleFactor) * xDiff);
if (layoutParams.leftMargin < 0) {
layoutParams.leftMargin = 0;
}
if (layoutParams.topMargin < 0) {
layoutParams.topMargin = 0;
}
layoutParams.width = (displayMetrics.widthPixels - layoutParams.leftMargin);
layoutParams.height = (displayMetrics.heightPixels - (layoutParams.topMargin * 2));
this.requestLayout();
/*final float dx = X - _xDelta;
final float dy = Y - _yDelta;
posX += dx;
posY += dy;
//matrix.postScale(scaleFactor, scaleFactor,0.0f,0.5f);
Bitmap bitmap = Bitmap.createBitmap((int)(displayMetrics.widthPixels - posX), (int)(displayMetrics.heightPixels - posY), Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
matrix.postTranslate(posX, posY);
canvas.setMatrix(matrix);
this.draw(canvas);
_xDelta = X;
_yDelta = Y;
invalidate();*/
break;
case MotionEvent.ACTION_UP:
isScrolling = false;
break;
case MotionEvent.ACTION_DOWN:
FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) this
.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = X - lParams.topMargin;
posX = 0;
posY = 0;
break;
}
return true;
}
return true;
}
}
You can call invalidate() in onTouch() to execute the code in onDraw() to redraw the view.
invalidate() will force a view to draw.
I'm trying to make a simple app, with an imageview that can be zoomed in and out with buttons, and when zoomed, user could move around it with his fingers.
I've been reading the whole day, here and there, found different solutions. But I can't seem to make my picture move. Thing is, I want to keep the program like it is now, but just maybe change the imageview OnTouchEvent.
I'm adding my code:
public class LoadMap extends Activity {
ZoomControls zoom;
ImageView img;
ImageButton linkButton;
float startX, startY;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_map);
zoom = (ZoomControls) findViewById(R.id.zoomControls1);
img = (ImageView) findViewById(R.id.imageView1);
linkButton = (ImageButton) findViewById(R.id.infopoint);
startX = img.getScaleX();
startY = img.getScaleY();
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
linkButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Uri uri = Uri.parse("just some site");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
zoom.setOnZoomInClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
float x = img.getScaleX();
float y = img.getScaleY();
img.setScaleX((float) (x+1));
img.setScaleY((float) (y+1));
}
});
zoom.setOnZoomOutClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
float x = img.getScaleX();
float y = img.getScaleY();
if((x>startX) & (y>startY)) {
img.setScaleX((float) (x-1));
img.setScaleY((float) (y-1));
}
}
});
}
Is there a way to just change some things inside this method without messing my app too much and is this even possible the way I'm trying to make it work or my program logic is wrong.
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
Sorry, if it has been asked before, but i searched alot and couldn't find anything specific for my 'problem'.
Thanks alot to whoever spends some time on my problem :).
try this code,
put this code on image setOnTouchListener,
img.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
LayoutParams layoutParams = (LayoutParams) img
.getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int x_cord = (int) event.getRawX();
int y_cord = (int) event.getRawY();
if (x_cord > windowwidth) {
x_cord = windowwidth;
}
if (y_cord > windowheight) {
y_cord = windowheight;
}
layoutParams.leftMargin = x_cord - 25;
layoutParams.topMargin = y_cord - 75;
img.setLayoutParams(layoutParams);
break;
default:
break;
}
return true;
}
});
I hope it will solve your problem..
Try using a custom ImageVIew
public class ZoomableImageView extends ImageView implements OnTouchListener {
private Context mContext;
final private float MAX_SCALE = 2f;
private Matrix mMatrix;
private final float[] mMatrixValues = new float[9];
// display width height.
private int mWidth;
private int mHeight;
private int mIntrinsicWidth;
private int mIntrinsicHeight;
private float mScale;
private float mMinScale;
private float mPrevDistance;
private boolean isScaling;
private int mPrevMoveX;
private int mPrevMoveY;
private GestureDetector mDetector;
final String TAG = "ScaleImageView";
public ZoomableImageView(Context context, AttributeSet attr) {
super(context, attr);
this.mContext = context;
initialize();
}
public ZoomableImageView(Context context) {
super(context);
this.mContext = context;
initialize();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
this.initialize();
}
#Override
public void setImageResource(int resId) {
super.setImageResource(resId);
this.initialize();
}
private void initialize() {
this.setScaleType(ScaleType.MATRIX);
this.mMatrix = new Matrix();
Drawable d = getDrawable();
if (d != null) {
mIntrinsicWidth = d.getIntrinsicWidth();
mIntrinsicHeight = d.getIntrinsicHeight();
setOnTouchListener(this);
}
mDetector = new GestureDetector(mContext,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
maxZoomTo((int) e.getX(), (int) e.getY());
cutting();
return super.onDoubleTap(e);
}
});
}
#Override
protected boolean setFrame(int l, int t, int r, int b) {
mWidth = r - l;
mHeight = b - t;
mMatrix.reset();
int rNorm = r - l;
mScale = (float) rNorm / (float) mIntrinsicWidth;
int paddingHeight = 0;
int paddingWidth = 0;
// scaling vertical
if (mScale * mIntrinsicHeight > mHeight) {
mScale = (float) mHeight / (float) mIntrinsicHeight;
mMatrix.postScale(mScale, mScale);
paddingWidth = (r - mWidth) / 2;
paddingHeight = 0;
// scaling horizontal
} else {
mMatrix.postScale(mScale, mScale);
paddingHeight = (b - mHeight) / 2;
paddingWidth = 0;
}
mMatrix.postTranslate(paddingWidth, paddingHeight);
setImageMatrix(mMatrix);
mMinScale = mScale;
zoomTo(mScale, mWidth / 2, mHeight / 2);
cutting();
return super.setFrame(l, t, r, b);
}
protected float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
protected float getScale() {
return getValue(mMatrix, Matrix.MSCALE_X);
}
public float getTranslateX() {
return getValue(mMatrix, Matrix.MTRANS_X);
}
protected float getTranslateY() {
return getValue(mMatrix, Matrix.MTRANS_Y);
}
protected void maxZoomTo(int x, int y) {
if (mMinScale != getScale() && (getScale() - mMinScale) > 0.1f) {
// threshold 0.1f
float scale = mMinScale / getScale();
zoomTo(scale, x, y);
} else {
float scale = MAX_SCALE / getScale();
zoomTo(scale, x, y);
}
}
public void zoomTo(float scale, int x, int y) {
if (getScale() * scale < mMinScale) {
return;
}
if (scale >= 1 && getScale() * scale > MAX_SCALE) {
return;
}
mMatrix.postScale(scale, scale);
// move to center
mMatrix.postTranslate(-(mWidth * scale - mWidth) / 2,
-(mHeight * scale - mHeight) / 2);
// move x and y distance
mMatrix.postTranslate(-(x - (mWidth / 2)) * scale, 0);
mMatrix.postTranslate(0, -(y - (mHeight / 2)) * scale);
setImageMatrix(mMatrix);
}
public void cutting() {
int width = (int) (mIntrinsicWidth * getScale());
int height = (int) (mIntrinsicHeight * getScale());
if (getTranslateX() < -(width - mWidth)) {
mMatrix.postTranslate(-(getTranslateX() + width - mWidth), 0);
}
if (getTranslateX() > 0) {
mMatrix.postTranslate(-getTranslateX(), 0);
}
if (getTranslateY() < -(height - mHeight)) {
mMatrix.postTranslate(0, -(getTranslateY() + height - mHeight));
}
if (getTranslateY() > 0) {
mMatrix.postTranslate(0, -getTranslateY());
}
if (width < mWidth) {
mMatrix.postTranslate((mWidth - width) / 2, 0);
}
if (height < mHeight) {
mMatrix.postTranslate(0, (mHeight - height) / 2);
}
setImageMatrix(mMatrix);
}
private float distance(float x0, float x1, float y0, float y1) {
float x = x0 - x1;
float y = y0 - y1;
return FloatMath.sqrt(x * x + y * y);
}
private float dispDistance() {
return FloatMath.sqrt(mWidth * mWidth + mHeight * mHeight);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (mDetector.onTouchEvent(event)) {
return true;
}
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
case MotionEvent.ACTION_POINTER_2_DOWN:
if (touchCount >= 2) {
float distance = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
mPrevDistance = distance;
isScaling = true;
} else {
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
}
case MotionEvent.ACTION_MOVE:
if (touchCount >= 2 && isScaling) {
float dist = distance(event.getX(0), event.getX(1),
event.getY(0), event.getY(1));
float scale = (dist - mPrevDistance) / dispDistance();
mPrevDistance = dist;
scale += 1;
scale = scale * scale;
zoomTo(scale, mWidth / 2, mHeight / 2);
cutting();
} else if (!isScaling) {
int distanceX = mPrevMoveX - (int) event.getX();
int distanceY = mPrevMoveY - (int) event.getY();
mPrevMoveX = (int) event.getX();
mPrevMoveY = (int) event.getY();
mMatrix.postTranslate(-distanceX, -distanceY);
cutting();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_2_UP:
if (event.getPointerCount() <= 1) {
isScaling = false;
}
break;
}
return true;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return super.onTouchEvent(event);
}
}
Try it that way:
img.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
return true;
}
if(event.getAction() == MotionEvent.ACTION_MOVE) {
RelativeLayout.LayoutParams mParams = (RelativeLayout.LayoutParams) img.getLayoutParams();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.leftMargin = x;
mParams.topMargin = y;
img.setLayoutParams(mParams);
}
return false;
}
});
You need to return true when the ACTION_DOWN event is triggered to indicate that you are interested in the subsequent calls relating to that same event.
I am developing an app which takes a picture from camera and then applies an imageview on top of it.
Now, I need to move and resize (with pinch) the imageview above the captured picture.
First I've implemented a simple drag behavior which worked well (I cannot use the drag event since min sdk level), but problems have came when I tried to add a pinch-to-zoom action too.
Right now I've tried several solutions found on stackoverflow, but none of them has worked for my scenario.
Here is the custom view that I'm using (found after some searchs)
https://dl.dropboxusercontent.com/u/230145/PanZoomView.java
but it has a weird behavior since when I try to do a zoom or drag it, the object gets crazy on the screen.
Anyone has an idea or solution for this?
EDIT
Finally I've solved with the following code:
Code from Java Class:
private ImageView mOrologio;
private FrameLayout mPhotoBox;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
...
...
private void bindViews(View rootView) {
...
mPhotoBox = (FrameLayout) rootView.findViewById(R.id.fotoBox);
mOrologio = (ImageView) rootView.findViewById(R.id.imgOrologio);
mOrologio.setOnTouchListener(this);
...
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
}
Code from XML Layout:
<FrameLayout
android:id="#+id/fotoBox"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/prev_list" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imgOrologio"
android:scaleType="matrix"
android:layout_gravity="right|center_vertical" />
</FrameLayout>
So basically I've learned this lesson:
FrameLayout is needed as rootView if you want to move/zoom your ImageView
If you want to move/zoom an ImageView like in my scenario you don't need to override the ImageView class but only handle the touch events.
Thank you all for replies and hope this solution will be usefull =)
You can use a custom image view above the current screen using a frame layout. You can use the custom class here will may help you
public class CustomZoomableImageView extends ImageView {
private Paint borderPaint = null;
private Paint backgroundPaint = null;
private float mPosX = 0f;
private float mPosY = 0f;
private float mLastTouchX;
private float mLastTouchY;
private static final int INVALID_POINTER_ID = -1;
private static final String LOG_TAG = "TouchImageView";
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
public CustomZoomableImageView (Context context) {
this(context, null, 0);
}
public CustomZoomableImageView (Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
// Existing code ...
public CustomZoomableImageView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Create our ScaleGestureDetector
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
borderPaint = new Paint();
borderPaint.setARGB(255, 255, 128, 0);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(4);
backgroundPaint = new Paint();
backgroundPaint.setARGB(32, 255, 255, 255);
backgroundPaint.setStyle(Paint.Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
/*
* (non-Javadoc)
*
* #see android.view.View#draw(android.graphics.Canvas)
*/
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, borderPaint);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, backgroundPaint);
if (this.getDrawable() != null) {
canvas.save();
canvas.translate(mPosX, mPosY);
Matrix matrix = new Matrix();
matrix.postScale(mScaleFactor, mScaleFactor, pivotPointX,
pivotPointY);
// canvas.setMatrix(matrix);
canvas.drawBitmap(
((BitmapDrawable) this.getDrawable()).getBitmap(), matrix,
null);
// this.getDrawable().draw(canvas);
canvas.restore();
}
}
/*
* (non-Javadoc)
*
* #see
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable
* )
*/
#Override
public void setImageDrawable(Drawable drawable) {
// Constrain to given size but keep aspect ratio
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
mLastTouchX = mPosX = 0;
mLastTouchY = mPosY = 0;
int borderWidth = (int) borderPaint.getStrokeWidth();
mScaleFactor = Math.min(((float) getLayoutParams().width - borderWidth)
/ width, ((float) getLayoutParams().height - borderWidth)
/ height);
pivotPointX = (((float) getLayoutParams().width - borderWidth) - (int) (width * mScaleFactor)) / 2;
pivotPointY = (((float) getLayoutParams().height - borderWidth) - (int) (height * mScaleFactor)) / 2;
super.setImageDrawable(drawable);
}
float pivotPointX = 0f;
float pivotPointY = 0f;
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
pivotPointX = detector.getFocusX();
pivotPointY = detector.getFocusY();
Log.d(LOG_TAG, "mScaleFactor " + mScaleFactor);
Log.d(LOG_TAG, "pivotPointY " + pivotPointY + ", pivotPointX= "
+ pivotPointX);
mScaleFactor = Math.max(0.05f, mScaleFactor);
invalidate();
return true;
}
}
And call the setImageDrawableMethod in the custom imageView
Are you trying to crop the image. I can not comment on your post but just answer. If so then let me know because you can use CROP intent to for what you are looking for. I have implemented it and it basically takes picture with camera... crops,zooms and drag .
Try this.
Something like below will do it.
#Override public boolean onTouch(View v,MotionEvent e)
{
tap=tap2=drag=pinch=none;
int mask=e.getActionMasked();
posx=e.getX();posy=e.getY();
float midx= img.getWidth()/2f;
float midy=img.getHeight()/2f;
int fingers=e.getPointerCount();
switch(mask)
{
case MotionEvent.ACTION_POINTER_UP:
tap2=1;break;
case MotionEvent.ACTION_UP:
tap=1;break;
case MotionEvent.ACTION_MOVE:
drag=1;
}
if(fingers==2){nowsp=Math.abs(e.getX(0)-e.getX(1));}
if((fingers==2)&&(drag==0)){ tap2=1;tap=0;drag=0;}
if((fingers==2)&&(drag==1)){ tap2=0;tap=0;drag=0;pinch=1;}
if(pinch==1)
{
if(nowsp>oldsp)scale+=0.1;
if(nowsp<oldsp)scale-=0.1;
tap2=tap=drag=0;
}
if(tap2==1)
{
scale-=0.1;
tap=0;drag=0;
}
if(tap==1)
{
tap2=0;drag=0;
scale+=0.1;
}
if(drag==1)
{
movx=posx-oldx;
movy=posy-oldy;
x+=movx;
y+=movy;
tap=0;tap2=0;
}
m.setTranslate(x,y);
m.postScale(scale,scale,midx,midy);
img.setImageMatrix(m);img.invalidate();
tap=tap2=drag=none;
oldx=posx;oldy=posy;
oldsp=nowsp;
return true;
}
public void onCreate(Bundle b)
{
super.onCreate(b);
img=new ImageView(this);
img.setScaleType(ImageView.ScaleType.MATRIX);
img.setOnTouchListener(this);
path=Environment.getExternalStorageDirectory().getPath();
path=path+"/DCIM"+"/behala.jpg";
byte[] bytes;
bytes=null;
try{
FileInputStream fis;
fis=new FileInputStream(path);
BufferedInputStream bis;
bis=new BufferedInputStream(fis);
bytes=new byte[bis.available()];
bis.read(bytes);
if(bis!=null)bis.close();
if(fis!=null)fis.close();
}
catch(Exception e)
{
ret="Nothing";
}
Bitmap bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length);
img.setImageBitmap(bmp);
setContentView(img);
}
For viewing complete program see here: Program to zoom image in android
I'm able to rotate image view on ACTION_DOWN.
but its not working for ACTION_MOVE & ACTION_UP.
public class RotateImage extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Rotate rotate;
rotate = new Rotate(this);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(rotate);
}
}
public class Rotate extends ImageView {
Paint paint;
int direction = 0;
private float x;
private float y;
int degree = 0;
float a, b, c;
private float centerX ;
private float centerY ;
private float newX ;
private float newY ;
public Rotate(Context context) {
super(context);
// TODO Auto-generated constructor stub
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(2);
paint.setStyle(Style.STROKE);
this.setImageResource(R.drawable.direction1);
}
#Override
protected void onDraw(Canvas canvas) {
int height = this.getHeight();
int width = this.getWidth();
centerX = width/2;
centerY = height/2;
canvas.rotate(direction, width / 2, height / 2);
super.onDraw(canvas);
}
public void setDirection(int direction) {
this.direction = direction;
this.invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println("now i'm in BEFORE calling MotionEvent.ACTION_MOVE ");
if (event.getAction() == MotionEvent.ACTION_DOWN) {
x = event.getX();
y = event.getY();
newX = centerX-x;
newY = centerY-y;
updateRotation( newX, newY);
}
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
x = event.getX();
y = event.getY();
newX = centerX-x;
newY = centerY-y;
updateRotation( newX, newY);
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
x = event.getX();
y = event.getY();
newX = centerX-x;
newY = centerY-y;
updateRotation( newX, newY);
}
return super.onTouchEvent(event);
}
private void updateRotation(float newX2, float newY2) {
// TODO Auto-generated method stub
degree = (int)Math.toDegrees(Math.atan2(newY, newX))-90;
setDirection(degree);
}
}
any suggestion would be appropriated
Return true instead of super.onTouchEvent(event) in your onTouchEvent method. This will inform the OS that you are handling the touch event allowing it to provide you with further touch events (e.g. MotionEvent.ACTION_MOVE).