How to Zoom a Text View in Scroll View? - java

I have seen a lots of code here which is helpful to zoom your textview but none of them work with my text because it is within scrollview. How can I get rid of this problem?
import android.app.Activity;
import android.os.Bundle;
import android.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;
public class Introduce extends Activity implements OnTouchListener{
final static float STEP = 200;
TextView mtxtRatio1,mtxtRatio2,mtxtRatio3,mtxtRatio4;
float mRatio = 1.0f;
int mBaseDist;
float mBaseRatio;
float fontsize = 13;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.introduce);
mtxtRatio1 = (TextView)findViewById(R.id.intro1);
mtxtRatio1.setTextSize(mRatio+13);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getPointerCount() == 2) {
int action = event.getAction();
int pureaction = action & MotionEvent.ACTION_MASK;
if (pureaction == MotionEvent.ACTION_POINTER_DOWN) {
mBaseDist = getDistance(event);
mBaseRatio = mRatio;
} else {
float delta = (getDistance(event) - mBaseDist) / STEP;
float multi = (float)Math.pow(2, delta);
mRatio = Math.min(1024.0f, Math.max(0.1f, mBaseRatio * multi));
mtxtRatio1.setTextSize(mRatio+13);
}
}
return true;
}
int getDistance(MotionEvent event) {
int dx = (int)(event.getX(0) - event.getX(1));
int dy = (int)(event.getY(0) - event.getY(1));
return (int)(Math.sqrt(dx * dx + dy * dy));
}
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return false;
}
}

Following is the way for implementing Pinch Zoom in TextView with/without ScrollView
MainActivity.java
public class MainActivity extends AppCompatActivity{
final static float STEP = 200;
float mRatio = 1.0f;
int mBaseDist;
float mBaseRatio;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
textViewData = (TextView).findViewById(R.id.tvContributeData);
textViewData.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getPointerCount() == 2) {
int action = event.getAction();
int pureaction = action & MotionEvent.ACTION_MASK;
if (pureaction == MotionEvent.ACTION_POINTER_DOWN) {
mBaseDist = getDistance(event);
mBaseRatio = mRatio;
} else {
float delta = (getDistance(event) - mBaseDist) / STEP;
float multi = (float) Math.pow(2, delta);
mRatio = Math.min(1024.0f, Math.max(0.1f, mBaseRatio * multi));
textViewData.setTextSize(mRatio + 13);
}
}
return true;
});
int getDistance(MotionEvent event) {
int dx = (int) (event.getX(0) - event.getX(1));
int dy = (int) (event.getY(0) - event.getY(1));
return (int) (Math.sqrt(dx * dx + dy * dy));
}
}
}

Use Polidea's zoomview, it works in a scrollview and has pinch zoom and double tap to zoom, one thing thought, I ended up disabling the pinch zoom and just using the double tap
https://github.com/Polidea/android-zoom-view
Put your TextView andany other Views you are using into a LinearLayout that lives on a ZoomView which lives on the ScrollView, e.g.:
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.polidea.ZoomView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="#+id/myLinearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.polidea.ZoomView>
</ScrollView>

Hopefully this will help others. This answer is from here and here.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
tv.setText(getString(R.string.hello_world));
scaleGestureDetector = new ScaleGestureDetector(this, new simpleOnScaleGestureListener());
tv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getPointerCount() == 1){
//stuff for 1 pointer
}else{ //when 2 pointers are present
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
scaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
scaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
return true;
}
});
}
The answer from here has the problem when text is resized even fingers are static (two fingers on the screen). What I did is add a check so that the textSize do not make any changes instantly.
private float safe;
public class simpleOnScaleGestureListener extends SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
float size = tv.getTextSize();
Log.d("TextSizeStart", String.valueOf(size));
//float factor = detector.getScaleFactor();
float factor = Math.max(0.5f, Math.min(detector.getScaleFactor(), 2f));
Log.d("Factor", String.valueOf(factor));
float product = size*factor;
Log.d("TextSize", String.valueOf(product));
safe = Math.abs(product - size);
if(product <= 100 && product >= 20 && safe < 3){
//tv.setText("factor= " +factor + "\n" + "product = \n" + size + " * " + factor + " \n= " + product +"\n" + getString(R.string.hello_world));
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, product);
}
size = tv.getTextSize();
Log.d("TextSizeEnd", String.valueOf(size));
return true;
}
}
You can play around with safe < 3 to your desired changes value.

I'm using this solution.
Crédits for Zoom Algorithm in this vídeo
Use a TextView without ScrollView, just use android:scrollbars="vertical"
<TextView
android:id="#+id/activity_content_text_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_weight="1"
android:gravity="fill"
android:textSize="8pt"
android:scrollbars="vertical"
/>
Java:
public class MainActivity extends Activity implements View.OnTouchListener {
private TextView textContent = null;
private final static float move = 200;
private float ratio = 1.0f;
private int baseDist;
private float baseRatio;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.teste_layout);
textContent = findViewById(R.id.activity_content_text_content);
textContent.setText("Lorem ipsum dolor sit amet......");
textContent.setMovementMethod(new ScrollingMovementMethod());
textContent.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getPointerCount() == 2){
int action = event.getAction();
int mainAction = action&MotionEvent.ACTION_MASK;
if(mainAction == MotionEvent .ACTION_POINTER_DOWN){
baseDist = getDisTance(event);
baseRatio = ratio;
} else {
float scale = (getDisTance(event)-baseDist)/move;
float factor = (float)Math.pow(2, scale);
ratio = Math.min(1024.0f, Math.max(0.1f, baseRatio*factor));
textContent.setTextSize(ratio+15);
}
} else {
return false;
}
return true;
}
private int getDisTance(MotionEvent event) {
int dx = (int) (event.getX(0)-event.getX(1));
int dy = (int) (event.getY(0)-event.getY(1));
return (int) (Math.sqrt(dx*dx+dy*dy));
}
}

Related

The view disappear after changing its width many times

I am trying to make simple Sticker View for Pictures Editing Project, but i am facing problem when I switch the sticker between the Views using:-
sticker.select(view);
when I select View for the first time its working good, but for the second time its change its position but the height and width still the width and height of the first View like the picture:-
full code:-
```class Sticker extends LinearLayout {
public Sticker(Context ctx) {
super(ctx);
LayoutInflater thisLL = getLayoutInflater();
View thisVV = thisLL.inflate(R.layout.sticker, null);
this.removeAllViews();
((LinearLayout) this).addView(thisVV);
this.setLayoutParams(new FrameLayout.LayoutParams((int) (ViewGroup.LayoutParams.WRAP_CONTENT),(int) (ViewGroup.LayoutParams.WRAP_CONTENT)));
selector = (LinearLayout)
thisVV.findViewById(R.id.selector);
/*rightP = (FrameLayout)
thisVV.findViewById(R.id.rightP);
bottomP = (FrameLayout)
thisVV.findViewById(R.id.bottomP);
pr = (LinearLayout)
thisVV.findViewById(R.id.pr);
pb = (LinearLayout)
thisVV.findViewById(R.id.pb);
*/
rotate = (ImageView)
thisVV.findViewById(R.id.rotate);
scale = (ImageView)
thisVV.findViewById(R.id.scale);
final Sticker stick = this;
rotate.setOnTouchListener(new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event){
int ev = event.getAction();
switch (ev) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
double r = (90 + (Math.atan2(event.getRawY() - (stick.getY()+(stick.getMeasuredHeight()/2)),event.getRawX() - (stick.getX()+(stick.getMeasuredWidth()/2))) * 180 / Math.PI));
if (-360 > r) {
r+=360;
}
r+=45;//to get smooth rotate
stick.setRotation((float)r);
selected.setRotation((float)r);
break;
} return true;
}
});
selector.setBackground(new GradientDrawable() { public GradientDrawable getIns(int a, int b, int c, int d) { this.setCornerRadius(a); this.setStroke(b, c); this.setColor(d); return this; } }.getIns((int)0, (int)2, 0xFF607D8B, Color.TRANSPARENT));
scale.setOnTouchListener(new OnTouchListener() {
PointF DivPT = new PointF(0,0);
PointF StartPT = new PointF();
private float initialTouchX;
float initialTouchY;
private int initialX;
private int initialY;
private int initialX2;
private int initialY2;
private int ex;
private int ey;
private int dw;
private int dh;
private float xx;
private float yy;
#Override public boolean onTouch(View _view, MotionEvent event) {
int n=event.getAction();
if(n==MotionEvent.ACTION_UP) drag=false;
if(n==MotionEvent.ACTION_DOWN){
drag=true;
xx=selected.getX();
yy=selected.getY();
//dw=stick.selected.getMeasuredWidth()-frame.getMeasuredWidth();
//dh=stick.selected.getMeasuredHeight()-frame.getMeasuredHeight()-rotate.getMeasuredHeight();
}
if (n != 0) {
if (n != 2) {
return true;
}
float cx = this.initialX + (float)(event.getRawX() - this.initialTouchX);
float cy = this.initialY + (float)(event.getRawY() - this.initialTouchY);
float cx2 = this.initialX + (float)(event.getRawX() - this.initialTouchX);
float cy2 = this.initialY + (float)(event.getRawY() - this.initialTouchY);
selected.setLayoutParams(new FrameLayout.LayoutParams((int) (cx),(int) (cy)));
select(selected);
//selector.setLayoutParams(new LinearLayout.LayoutParams((int) (cx2),(int) (cy2)));
}
this.initialX = selected.getMeasuredWidth();
this.initialY = selected.getMeasuredHeight();
this.initialX2= selector.getMeasuredWidth();
this.initialY2= selector.getMeasuredHeight();
this.initialTouchX = event.getRawX();
this.initialTouchY = event.getRawY();
return true;
}});
}
LinearLayout selector;
FrameLayout rightP;
FrameLayout bottomP;
ImageView rotate;
ImageView scale;
LinearLayout pr;
LinearLayout pb;
public View selected;
public View previous;
boolean drag = false;
public void select(View v){
this.selected=v;
try {
if (!(v.equals(previous))){
try {
((ViewGroup)this.getParent()).removeView(this);
} catch (Exception exp65676) {
}
((ViewGroup) ((ViewGroup)v.getParent())).addView(this);
}
this.selector.getLayoutParams().width = ((int)(v.getMeasuredWidth()));
this.selector.getLayoutParams().height = ((int)(v.getMeasuredHeight()));
/**/
/*
this.selector.setLayoutParams(new LinearLayout.LayoutParams((int) (0),(int) (0)));
*/
previous=v;
((View)(v.getParent())).invalidate();
float top=((LinearLayout.LayoutParams)(this.selector.getLayoutParams())).topMargin;
float left =((LinearLayout.LayoutParams)(this.selector.getLayoutParams())).leftMargin;
setX(v.getX()-rotate.getMeasuredWidth());
setY(v.getY()-top);
setRotation((float)(v.getRotation()));
} catch (Exception e) {
SketchwareUtil.showMessage(getApplicationContext(), (e.toString()));
}
}
private PointF DownPT = new PointF();
private PointF StartPT = new PointF();
private float SX;
private float SY;
private float rotation;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//setRotation(0);
//selected.setRotation(0);
PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
setX((int)(StartPT.x+mv.x));
setY((int)(StartPT.y+mv.y));
selected.setX((int)((StartPT.x+mv.x)));
selected.setY((int)(StartPT.y+mv.y+SY));
StartPT = new PointF(getX(),getY());
select(selected);
break;
case MotionEvent.ACTION_DOWN :
DownPT.x = event.getX();
DownPT.y = event.getY();
rotation=getRotation();
//setRotation(0);
//setAlpha(0);
//drag=true;
StartPT = new PointF(getX(),getY());
//SX=getX()-selected.getX();
SX=rotate.getMeasuredWidth();
SY=getY()-selected.getY();
break;
case MotionEvent.ACTION_UP :
setRotation(rotation);
setAlpha(1);
selected.setRotation(rotation);
select(selected);
drag=false;
break;
default : break;
} return true;
}
}```
xml file:
``` <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right|bottom"
android:orientation="horizontal">
<ImageView
android:id="#+id/rotate"
android:focusable="false"
android:layout_width="20dp"
android:layout_height="match_parent"
android:src="#drawable/rotate"
android:scaleType="fitStart" />
<LinearLayout
android:id="#+id/selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
<ImageView
android:id="#+id/scale"
android:focusable="false"
android:layout_width="20dp"
android:layout_height="match_parent"
android:src="#drawable/scale"
android:scaleType="fitEnd"
android:layout_gravity="bottom" />
</LinearLayout>
</LinearLayout>```
}
LinearLayout selector;
FrameLayout rightP;
FrameLayout bottomP;
ImageView rotate;
ImageView scale;
LinearLayout pr;
LinearLayout pb;
public View selected;
public View previous;
boolean drag = false;
public void select(View v){
this.selected=v;
try {
if (!(v.equals(previous))){
try {
((ViewGroup)this.getParent()).removeView(this);
} catch (Exception exp65676) {
}
((ViewGroup) ((ViewGroup)v.getParent())).addView(this);
}
this.selector.getLayoutParams().width = ((int)(v.getMeasuredWidth()));
this.selector.getLayoutParams().height = ((int)(v.getMeasuredHeight()));
/**/
/*
this.selector.setLayoutParams(new LinearLayout.LayoutParams((int) (0),(int) (0)));
*/
previous=v;
((View)(v.getParent())).invalidate();
float top=((LinearLayout.LayoutParams)(this.selector.getLayoutParams())).topMargin;
float left =((LinearLayout.LayoutParams)(this.selector.getLayoutParams())).leftMargin;
setX(v.getX()-rotate.getMeasuredWidth());
setY(v.getY()-top);
setRotation((float)(v.getRotation()));
} catch (Exception e) {
SketchwareUtil.showMessage(getApplicationContext(), (e.toString()));
}
}
private PointF DownPT = new PointF();
private PointF StartPT = new PointF();
private float SX;
private float SY;
private float rotation;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//setRotation(0);
//selected.setRotation(0);
PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
setX((int)(StartPT.x+mv.x));
setY((int)(StartPT.y+mv.y));
selected.setX((int)((StartPT.x+mv.x)));
selected.setY((int)(StartPT.y+mv.y+SY));
StartPT = new PointF(getX(),getY());
select(selected);
break;
case MotionEvent.ACTION_DOWN :
DownPT.x = event.getX();
DownPT.y = event.getY();
rotation=getRotation();
//setRotation(0);
//setAlpha(0);
//drag=true;
StartPT = new PointF(getX(),getY());
//SX=getX()-selected.getX();
SX=rotate.getMeasuredWidth();
SY=getY()-selected.getY();
break;
case MotionEvent.ACTION_UP :
setRotation(rotation);
setAlpha(1);
selected.setRotation(rotation);
select(selected);
drag=false;
break;
default : break;
} return true;
}
}

ViewPager Disable Swiping

I have a ViewPager which has 3 fragments. Fragment A on the left, B in the middle and C on the right. Fragment C has a ListView which fills the whole width of the screen. I implemented a swipe listener on my ListView items using the following code:
SWIPE DETECTOR :
public class SwipeDetector implements View.OnTouchListener {
public static enum Action {
LR, // Left to Right
RL, // Right to Left
TB, // Top to bottom
BT, // Bottom to Top
None // when no action was detected
}
private static final String logTag = "SwipeDetector";
private static final int MIN_DISTANCE = 100;
private static final int VERTICAL_MIN_DISTANCE = 80;
private static final int HORIZONTAL_MIN_DISTANCE = 80;
private float downX, downY, upX, upY;
private Action mSwipeDetected = Action.None;
public boolean swipeDetected() {
return mSwipeDetected != Action.None;
}
public Action getAction() {
return mSwipeDetected;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
downX = event.getX();
downY = event.getY();
mSwipeDetected = Action.None;
return false; // allow other events like Click to be processed
}
case MotionEvent.ACTION_MOVE: {
upX = event.getX();
upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
// horizontal swipe detection
if (Math.abs(deltaX) > HORIZONTAL_MIN_DISTANCE) {
// left or right
if (deltaX < 0) {
// Log.i(logTag, "Swipe Left to Right");
mSwipeDetected = Action.LR;
return true;
}
if (deltaX > 0) {
// Log.i(logTag, "Swipe Right to Left");
mSwipeDetected = Action.RL;
return true;
}
} else
// vertical swipe detection
if (Math.abs(deltaY) > VERTICAL_MIN_DISTANCE) {
// top or down
if (deltaY < 0) {
Log.i(logTag, "Swipe Top to Bottom");
mSwipeDetected = Action.TB;
return false;
}
if (deltaY > 0) {
Log.i(logTag, "Swipe Bottom to Top");
mSwipeDetected = Action.BT;
return false;
}
}
return true;
}
}
return false;
}}
I use it in the following way :
listView.setOnTouchListener(swipeDetector);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Log.d("CLICKED", "CLICKED");
if (swipeDetector.swipeDetected()) {
Log.d("SWIPING", "SWIPING");
Log.d("ACTION", swipeDetector.getAction().toString());
final Button del = (Button) view.findViewById(R.id.delete_button);
if (swipeDetector.getAction() == SwipeDetector.Action.LR) {
Log.d("LEFT TO RIGHT", "Left to right");
This works perfectly fine with Activities. However, the problem now is that when I swipe, it assumes I am swiping in the ViewPager and takes me back to the middle fragment. Is there a way to disable the ViewPager swiping on this ListView or change the focus so that this works?

Rotating around two points in android not working

I have two seperate (x, y) points that I want to use to apply rotation to a view.
The first point is fixed, and I find the values of it fairly easily (for example 200,200). My second point is where a TOUCH is present, so I grab the RawX and RawY points easily as well. I feed these two points into this method that I found on another stack overflow question.
private float findRotation(int firstPointX, int firstPointY, int secondPointX, int secondPointY) {
double delta_x = (firstPointX - secondPointX);
double delta_y = (firstPointY - secondPointY);
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
and I use the return of that to set the rotation of a View. Like so myView.setRotation(...). The result ends up being a crazy spinning view while I move the cursor/finger on the screen. Any ideas?
The two points I'm grabbing seem to be correct, leaving me guessing that maybe the findRotation method is incorrect.
My activity:
public class MainActivity extends Activity {
ImageView imageView;
ImageView dragHandle;
RelativeLayout layout;
int rememberAngle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
dragHandle = (ImageView) findViewById(R.id.imageView2);
layout = (RelativeLayout) findViewById(R.id.relativeLayout2);
resize(dragHandle);
}
public void resize(ImageView resizeButton) {
resizeButton.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
int[] locationOfLayout = new int[2];
int[] locationOfDrag = new int[2];
layout.getLocationOnScreen(locationOfLayout);
dragHandle.getLocationOnScreen(locationOfDrag);
int firstPointX = locationOfLayout[0];
int firstPointY = locationOfLayout[1];
int secondPointX = dragHandle.getWidth() / 2 + locationOfDrag[0];
int secondPointY = dragHandle.getHeight() / 2 + locationOfDrag[1];
rememberAngle = (int) findRotation(firstPointX, firstPointY, secondPointX, secondPointY) + layout.getRotation();
} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
int[] locationOfLayout = new int[2];
int[] locationOfDrag = new int[2];
layout.getLocationOnScreen(locationOfLayout);
dragHandle.getLocationOnScreen(locationOfDrag);
int centerXOnLayout = layout.getWidth() / 2 + locationOfLayout[0];
int centerYOnLayout = layout.getHeight() / 2 + locationOfLayout[1];
int centerXOnDrag = dragHandle.getWidth() / 2 + locationOfDrag[0];
int centerYOnDrag = dragHandle.getHeight() / 2 + locationOfDrag[1];
layout.setRotation(findRotation(centerXOnLayout, centerYOnLayout, centerXOnDrag, centerYOnDrag) - rememberAngle);
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
});
}
private float findRotation(int firstPointX, int firstPointY, int secondPointX, int secondPointY) {
double delta_x = (secondPointX - firstPointX);
double delta_y = (secondPointY - firstPointY);
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
}
My XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/relativeLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/ic_launcher" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_below="#+id/imageView1"
android:layout_toRightOf="#+id/imageView1"
android:src="#drawable/meanicons" />
</RelativeLayout>
</RelativeLayout>
public void resize(ImageView resizeButton) {
resizeButton.setOnTouchListener(new View.OnTouchListener() {
float startAngle;
float zeroAngle;
int firstPointX;
int firstPointY;
public boolean onTouch(View v, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
int[] locationOfLayout = new int[2];
int[] locationOfDrag = new int[2];
layout.getLocationOnScreen(locationOfLayout);
dragHandle.getLocationOnScreen(locationOfDrag);
firstPointX = locationOfLayout[0];
firstPointY = locationOfLayout[1];
int secondPointX = motionEvent.getRawX();
int secondPointY = motionEvent.getRawY();
zeroAngle = findRotation(firstPointX, firstPointY, secondPointX, secondPointY) // remember "zero" angle
startAngle = layout.getRotation(); // remember angle at which layout is rotated at the start
} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
layout.setRotation(findRotation(firstPointX, firstPointY, motionEvent.getRawX(), motionEvent.getRawY()) - zeroAngle + startAngle); // rotate relative to start and zero angle
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
}
return true;
}
});
}
private float findRotation(int firstPointX, int firstPointY, int secondPointX, int secondPointY) {
double delta_x = (secondPointX - firstPointX);
double delta_y = (secondPointY - firstPointY);
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}

How does pinch zoom work with panning for image in Android

Goal
An activity is made to view image, we can pinch zoom or pan the image. The image is centered in the screen in the beginning. Pinch zoom is centered at the center of the image, even after the image is panned somewhere else in the screen.
The image for displaying is downloaded from a given URL, and the URL is passed from extra of an intent to start the image viewing activity.
Pinch zoom is implemented by postScale(), pan by postTranslate().
Problem
After panning the image somewhere, the pinch-zoom center is still at the center of the screen. Tried to follow the center of the image when it's been moved to a new place but my code doesn't work that way. Please give some idea.
The image downloading and panning work well.
Code
activity_image_viewer_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:background="#color/MyPureBlack" >
<LinearLayout
android:id="#+id/progressbar_wrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ProgressBar
android:id="#+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_gravity="center" >
</ProgressBar>
</LinearLayout>
<ImageView
android:id="#+id/image_viewer"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/MyPureBlack"
android:scaleType="matrix" >
</ImageView>
</LinearLayout>
</FrameLayout>
ActivityImageViewer.java
package com.com2us.hubapp.android;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import org.apache.http.util.ByteArrayBuffer;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
public class ActivityImageViewer extends Activity {
File imageFile = null;
// Matrices for pinch zoom and pan
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
Matrix savedMatrixZoom = new Matrix();
// State of motion event
static final int NONE = 0;
static final int PAN = 1;
static final int PINCH_ZOOM = 2;
int mode = NONE;
// The first pointer down
PointF start = new PointF();
// The center of the image (Failed to track it when the image has been moved)
PointF centerOfImage = new PointF();
// oldest is the Cartesian distance between first two pointers when the second pointer is down
float oldDist = 1f;
// MIN_SCALE/MAX_SCALE is the min/max scale factor
private final float MIN_SCALE = 0.5f;
private final float MAX_SCALE = 3.0f;
// TOUCH_SENSITIVE is the minimum Cartesian distance between the first two pointers that triggers the pinch zoom
private final float TOUCH_SENSITIVE = 10.0f;
private final float SPACING_LEFT_AND_RIGHT = 30.0f;
private final float SPACING_TOP_AND_BOTTOM = 30.0f;
// The ImageView widget
private ImageView image_viewer;
// The progress bar shows what current progress is before the image downloading is completed
private ProgressBar progressbar;
private LinearLayout progressbar_wrapper;
// An async task that downloads the image from a given URL
private DownloadFilesTask downloadFilesTask;
private class DownloadFilesTask extends AsyncTask<String, Integer, Bitmap> {
protected Bitmap doInBackground(String... urls) {
InputStream input = null;
OutputStream output = null;
try {
URL url = new URL(urls[0]);
URLConnection connection = url.openConnection();
connection.connect();
int lenghtOfFile = connection.getContentLength();
// download the file
InputStream is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 8190);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
Bitmap bmp = BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
//final int percent = (int) (total * 100 / lenghtOfFile);
//publishProgress(percent);
//lenghtOfFile
return bmp;
} catch (Exception e) {
} finally {
try {
if (output != null)
output.close();
output = null;
} catch (IOException e) {
}
try {
if (input != null)
input.close();
input = null;
} catch (IOException e) {
}
}
return null;
} // protected Bitmap doInBackground(String... urls) {}
protected void onProgressUpdate(Integer... progress) {
progressbar.setProgress(progress[0]);
}
protected void onPostExecute(Bitmap bmp) {
if (bmp != null) {
final AlphaAnimation animationAfter = new AlphaAnimation(0.0f, 1.0f);
animationAfter.setDuration(300);
animationAfter.setFillEnabled(true);
animationAfter.setFillAfter(true);
image_viewer.setAnimation(animationAfter);
image_viewer.setImageBitmap(bmp);
ViewTreeObserver viewTreeObserver = image_viewer.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Drawable drawable = image_viewer.getDrawable();
int dx = (image_viewer.getWidth() - drawable.getIntrinsicWidth()) / 2;
int dy = (image_viewer.getHeight() - drawable.getIntrinsicHeight()) / 2;
matrix.postTranslate(dx, dy);
image_viewer.setImageMatrix(matrix);
}
});
progressbar_wrapper.setVisibility(View.GONE);
image_viewer.setVisibility(View.VISIBLE);
} else {
android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, 2000);
}
} // End of protected void onPostExecute(Bitmap bmp) {}
} // End of private class DownloadFilesTask extends AsyncTask<String, Integer, Bitmap> {}
// These are activity life cycle handling
// onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
//setTheme(R.style.HubTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_viewer);
progressbar_wrapper = (LinearLayout) findViewById(R.id.progressbar_wrapper);
image_viewer = (ImageView) findViewById(R.id.image_viewer);
progressbar = (ProgressBar) findViewById(R.id.progressbar);
image_viewer.setOnTouchListener(new MyOnTouchListener());
final String uriForImage = getIntent().getStringExtra("url");
downloadFilesTask = new DownloadFilesTask();
downloadFilesTask.execute(uriForImage);
}
// onStart
#Override
protected void onStart() {
super.onStart();
}
// onResume
#Override
protected void onResume() {
super.onResume();
}
// onPause
#Override
protected void onPause() {
super.onPause();
}
// onStop
#Override
protected void onStop() {
super.onStop();
}
// onRestart
#Override
protected void onRestart() {
super.onRestart();
}
// onDestroy
#Override
protected void onDestroy() {
super.onDestroy();
if (imageFile != null) {
try {
Drawable drawable = image_viewer.getDrawable();
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
bitmap.recycle();
drawable = null;
bitmapDrawable = null;
bitmap = null;
} catch (NullPointerException e) {
}
}
}
// onKeyDown
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
this.onBackPressed();
}
return true;
}
// onBackPressed
public void onBackPressed() {
finish();
}
// onConfigurationChanged
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.equals(Configuration.ORIENTATION_LANDSCAPE)) {
} else if (newConfig.equals(Configuration.ORIENTATION_PORTRAIT)) {
}
}
// onLowMemory
#Override
public void onLowMemory() {
super.onLowMemory();
finish();
}
// Get the Cartesian distance between the first two pointers
private float spacing(MotionEvent event) {
float x = 0;
float y = 0;
try {
Method getX = MotionEvent.class.getMethod("getX", Integer.TYPE);
Method getY = MotionEvent.class.getMethod("getX", Integer.TYPE);
// x = event.getX(0) - event.getX(1);
// y = event.getY(0) - event.getY(1);
float x1 = (Float) getX.invoke(event, 0);
float x2 = (Float) getX.invoke(event, 1);
x = x1 - x2;
float y1 = (Float) getY.invoke(event, 0);
float y2 = (Float) getY.invoke(event, 1);
y = y1 - y2;
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
return FloatMath.sqrt(x * x + y * y);
}
// Some flags set manually for convenience
private final int MotionEvent_ACTION_MASK = 255; // that is 0xFF or 11111111
private final int MotionEvent_ACTION_POINTER_DOWN = 5; // that is 101
private final int MotionEvent_ACTION_POINTER_UP = 6; // that is 110
private class MyOnTouchListener implements OnTouchListener {
// onTouch
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
Drawable drawable = view.getDrawable();
if (drawable == null)
return true;
switch (event.getAction() & MotionEvent_ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = PAN;
break;
case MotionEvent_ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > TOUCH_SENSITIVE) {
savedMatrix.set(matrix);
mode = PINCH_ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent_ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == PAN) {
// /////////////////////////////////////////
matrix.set(savedMatrix);
float[] matrixValues = new float[9];
Rect viewRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
matrix.getValues(matrixValues);
float currentY = matrixValues[Matrix.MTRANS_Y];
float currentX = matrixValues[Matrix.MTRANS_X];
float currentScale = matrixValues[Matrix.MSCALE_X];
float currentHeight = drawable.getIntrinsicHeight() * currentScale;
float currentWidth = drawable.getIntrinsicWidth() * currentScale;
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
float newX = currentX + dx;
float newY = currentY + dy;
RectF drawingRect = new RectF(newX, newY, newX + currentWidth, newY + currentHeight);
float diffUp = Math.min(viewRect.bottom - drawingRect.bottom, viewRect.top - drawingRect.top) - SPACING_TOP_AND_BOTTOM;
float diffDown = Math.max(viewRect.bottom - drawingRect.bottom, viewRect.top - drawingRect.top) + SPACING_TOP_AND_BOTTOM;
float diffLeft = Math.min(viewRect.left - drawingRect.left, viewRect.right - drawingRect.right) - SPACING_LEFT_AND_RIGHT;
float diffRight = Math.max(viewRect.left - drawingRect.left, viewRect.right - drawingRect.right) + SPACING_LEFT_AND_RIGHT;
if (diffUp > 0) {
dy += diffUp;
}
if (diffDown < 0) {
dy += diffDown;
}
if (diffLeft > 0) {
dx += diffLeft;
}
if (diffRight < 0) {
dx += diffRight;
}
matrix.postTranslate(dx, dy);
} else if (mode == PINCH_ZOOM) {
float newDist = spacing(event);
if (newDist > TOUCH_SENSITIVE) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
// Get the center of the image. (Failed to get it when image has been moved)
Rect viewRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
centerOfImage.x = viewRect.centerX();
centerOfImage.y = viewRect.centerY();
float[] f = new float[9];
Matrix tmp = new Matrix(matrix);
tmp.postScale(scale, scale, centerOfImage.x, centerOfImage.y);
tmp.getValues(f);
float scaleX = f[Matrix.MSCALE_X];
if (scaleX < MIN_SCALE || scaleX > MAX_SCALE) {
matrix.set(savedMatrixZoom);
} else {
matrix.postScale(scale, scale, centerOfImage.x, centerOfImage.y);
savedMatrixZoom.set(matrix);
}
}
}
break;
}
view.setImageMatrix(matrix);
return true;
} // End of public boolean onTouch(View v, MotionEvent event) {}
} // End of private class MyOnTouchListener implements OnTouchListener {}
} // End of public class ActivityImageViewer extends Activity {}
You can use the Scale Gesture Detector for pinch to zoom. Instead of creating pinch to zoom from scratch you can do something like following,
public class MyCustomView extends View {
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public MyCustomView(Context mContext){
...
// View code goes here
...
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleFactor, mScaleFactor);
...
// onDraw() code goes here
...
canvas.restore();
}
private class ScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
}
Note : Your translation will reside in onDraw method to scale an image.

Unable to zoom in the bus route map

i have a bus route map as an image.
using the zoom controller the image is zooming out but not zooming in
please look at my code and let me know the change to be done make it working..
i am developing my app on Gingerbread i.e API 10
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.ZoomControls;
public class Busmaps extends Activity {
ImageView img;
ZoomControls zoom;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.busmaps);
img = (ImageView) findViewById(R.id.imageViewmaps1);
zoom = (ZoomControls) findViewById(R.id.zoomControls1);
zoom.setOnZoomInClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int w = img.getWidth();
int h = img.getHeight();
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(w + 50, h + 50);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
img.setLayoutParams(params);
}
});
zoom.setOnZoomOutClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int w = img.getWidth();
int h = img.getHeight();
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(w - 50, h - 50);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
img.setLayoutParams(params);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.bus_map_zoom, menu);
return true;
}
}
my xml says for bus route image:-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageViewmaps1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/map" />
<ZoomControls
android:id="#+id/zoomControls1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
/>
</RelativeLayout>
how should i make my zoom control working for both Zoom in and Zoom out.
Please replace your Zoom in and ZoomOut listener with the following:
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 View.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));
}
});
}
Alternate option is to load the image in the Web view which has build in zoom controller.
As follows:
String page = "<html><body><center><img src=\""+path to your image+"\"/></center></body></html>";
webView.loadDataWithBaseURL("fake",page, "text/html", "UTF-8","");
If you dont want to use the build in web view zoom controller then you can just place your own buttons and apply zoom in and zoom out on web view as follows:
webView.setInitialScale(ZOOM_LEVEL);
public class Busmap extends Activity implements OnTouchListener
{
private static final String TAG = "Touch";
#SuppressWarnings("unused")
private static final float MIN_ZOOM = 1f,MAX_ZOOM = 1f;
// These matrices will be used to scale points of the image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// The 3 states (events) which the user is trying to perform
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// these PointF objects are used to record the point(s) the user is touching
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.busmaps);
ImageView view = (ImageView) findViewById(R.id.imageViewmaps1);
view.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event)
{
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN: // first finger down only
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG"); // write to LogCat
mode = DRAG;
break;
case MotionEvent.ACTION_UP: // first finger lifted
case MotionEvent.ACTION_POINTER_UP: // second finger lifted
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_POINTER_DOWN: // first and second finger down
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 5f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG)
{
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); // create the transformation in the matrix of points
}
else if (mode == ZOOM)
{
// pinch zooming
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 5f)
{
matrix.set(savedMatrix);
scale = newDist / oldDist; // setting the scaling of the
// matrix...if scale > 1 means
// zoom in...if scale < 1 means
// zoom out
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix); // display the transformation on screen
return true; // indicate event was handled
}
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);
}

Categories