I have a HorizontalScrollView
in which there are multiple views inside it. I have implemented pinch zoom gesture
in which multiple views, which are between my two fingers, are scaled.
But I am facing one glitch. When I am doing pinch zoom, the mid point of pinch zoom is moving but for user experience I want this point to be remain fixed and other things should adjust itself while scaling so that mid point remains static.
Can someone tell me how to do it.
onDraw()
method of custom view is
Rect r =new Rect(0, 0, 700, 40);
public void onDraw(Canvas canvas) {
float kk=mScaleFactor; //this variable will be set by pinch zoom event and represent how much to scale
sf*=kk; // this view must scale according to previous scaling
canvas.save();
canvas.scale(sf , 1);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
canvas.drawRect(r, paint);
width=sf*700;
canvas.restore();
requestLayout(); //this will change view width to fit the expanded rectangle
}
onMeasure method is called on requestLayout
@Override protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
setMeasuredDimension((int)width, 340);
}
The above custom view is called 12 times by a subclass of HorizontalScrollView
. So there are 12 child views of HorizontalScrollview
.
In this class I am doing the following things
Detecting the touch coordinates of two fingers.
Calculating the index of child view on which first finger is touched.
Calculating the index of child view on which second finger is touched.
Passing scale factor of pinch zoom to all the child views between start and last. These two indices are calculated in previous two step.
And finally invalidate() is called on the child view. So child can scale itself according to scale factor passed by parent view.
But there is one problem here. The mid point of two finger should remain static and other things should adjust during scaling. But my mid point is moving with scaling.
Can someone help me in doing this. Please tell me if any part of code is require.
Code of gesture listener of HorizontalScrollview
is
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
ViewGroup pp=takeparent(); //give object of this class
for(int i=start;i<=last;i++)
{
LinearLayout ll=(LinearLayout)pp.getChildAt(i);
DrawView view=(DrawView)ll.findViewById(R.id.drawview);
view.mScaleFactor=mScaleFactor;
view.invalidate();
view.donesf=true;
}
Sample app of mine
Edits as suggested in comments:
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
ViewGroup pp=takeparent(); pp contains all the custom child views
//start and last are indices of range of child for which we have to apply gesture
for(int i=start;i<=last;i++)
{
LinearLayout ll=(LinearLayout)pp.getChildAt(i);
DrawView view=(DrawView)ll.findViewById(R.id.drawview);
view.mScaleFactor=mScaleFactor;
view.pivotx=detector.getFocusX()+view.getLeft();
view.pivoty=detector.getFocusY()+view.getTop();
view.invalidate();
}
return true;
}
This is custom view onDraw method
public void onDraw(Canvas canvas) {
sf=mScaleFactor //this scale factor is set by parent view
width=sf*700; //this width will be use to rescale the view's width in requestLayout()
canvas.save();
canvas.scale(sf,1,pivotx,pivoty); //pivotX and pivotY are also set by parent's onScale method of gestureListener
canvas.drawRect(r, paint);
requestLayout();
canvas.restore();
}
For a view you are scaling, you can set the "invariant" point that should not move using
setPivotX()
andsetPivotY()
. When I use aScaleGestureDetector
, I use thefocus
point for this. For example:I'm not sure if you want to do this with all of the children, or just the parent view, in your case, but usually you just need to do this to a single "parent" view and it will work properly for the children of that view.
Related to this, I didn't quite understand why you're passing the scaling factor down to each child when scaling the parent view will scale all of its children too. Maybe you just need to add a single
FrameLayout
(or some otherViewGroup
descendent) into yourHorizontalScrollView
that hosts the children and then just scale that (after setting its pivot appropriately)?Updated re comment
Given that you only want to scale the views in the pinched region between the fingers, I believe you have a couple options, depending on your app:
1) Dynamically add just those views to an intermediate
FrameLayout
which gets scaled and has its pivot set, leaving the non-scaled views as direct children of theHorizontalScrollView
; or2) Passing down the focus point to each scaled child view after first adjusting for the child's position. For example, assuming your
DrawView
either directly or indirectly inherits fromandroid.view.View
, which I would strongly recommend, then you can do something like: