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);
}
Related
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;
}
}
I am a newbie in android development. I am trying to create an image gallery . While implementing viewpager, I put imageview outside viewpager but because of that I am not able to make zoom and pinch and viewpager work at same time. How can I put imageview inside viewpager ?
FullImageActivity.java:
public class FullImageActivity extends AppCompatActivity {
ImageView images;
int position;
int folderPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_image);
Intent i = getIntent();
PhotoView photoView = (PhotoView) findViewById(R.id.photo_view);
photoView.setVisibility(View.GONE);
// Selected image id
position = i.getExtras().getInt("id");
folderPosition = i.getExtras().getInt("folderPosition");
Bundle extras = getIntent().getExtras();
String value = extras.getString("abc");
Glide.with(FullImageActivity.this)
.load(value)
.skipMemoryCache(false)
.into(photoView);
ViewPager mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(new TouchImageAdapter(this,al_images, folderPosition));
mViewPager.setCurrentItem(position);
}
}
activity_full_image.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/jazzy_pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.chrisbanes.photoview.PhotoView
android:id="#+id/photo_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
TouchImageAdapter.java
class TouchImageAdapter extends PagerAdapter {
Context context;
String filename;
ArrayList<Model_images> al_menu = new ArrayList<>();
int position,int_position;
public TouchImageAdapter(Context context,ArrayList<Model_images> al_menu, int position){
this.al_menu = al_menu;
this.context = context;
this.int_position = position;
}
#Override
public int getCount() {
return al_menu.get(int_position).getAl_imagepath().size();
}
#Override
public View instantiateItem(ViewGroup container, int position) {
ImageView img = new ImageView(container.getContext());
ViewGroup.LayoutParams lp= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
img.setLayoutParams(lp);
img.setImageDrawable(getImageFromSdCard(filename,position));
container.addView(img, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
return img;
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
public Drawable getImageFromSdCard(String imageName,int position) {
Drawable d = null;
try {
String path = al_menu.get(int_position).getAl_imagepath().get(position)
+ "/";
Bitmap bitmap = BitmapFactory.decodeFile(path);
d = new BitmapDrawable(context.getResources(),bitmap);
} catch (IllegalArgumentException e) {
// TODO: handle exception
}
return d;
}
}
Use this custom TouchImageView instead of imageview
public class TouchImageView extends ImageView {
private void stopInterceptEvent()
{
getParent().requestDisallowInterceptTouchEvent(true);
}
private void startInterceptEvent()
{
getParent().requestDisallowInterceptTouchEvent(false);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix.setTranslate(1f, 1f);
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
stopInterceptEvent();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float scaleWidth = Math.round(origWidth * saveScale);
float scaleHeight = Math.round(origHeight * saveScale);
if (scaleWidth < width) {
deltaX = 0;
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
} else if (scaleHeight < height) {
deltaY = 0;
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
} else {
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
}
if(deltaX == 0)
startInterceptEvent();
else
stopInterceptEvent();
matrix.postTranslate(deltaX, deltaY);
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
startInterceptEvent();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
}
Also change your activity_full_xml like this
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TouchImageView
android:id="#+id/photo_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerCrop"/>
</android.support.v4.view.ViewPager>
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));
}
}
I have tried everything to get my ScrollView and SlidingUpPanelLayout to function how Google Maps app handles it. (video example)
This is the only link that seems to actually be working for people, but it is not working for me.
https://stackoverflow.com/a/22720204/172336
I've followed it 4 times today and every time my ScrollView scrolls and my SlidingUpPanelLayout does absolutely nothing. (It sits there)
Here is the code I am adding to the code from the SlidingUpPanelLayout as seen on GitHub.
activity_main.xml
<com.bouy76.sportsmantracker.SlidingUpPanelLayout
android:id="#+id/sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:gravity="bottom"
app:panelHeight="68dp"
app:dragView="#+id/drag_view"
app:scrollView="#+id/scroll_view"> <!-- attrs.xml tag -->
<!-- MAIN CONTENT -->
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="top">
<com.nutiteq.MapView
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<!-- SLIDING LAYOUT -->
<RelativeLayout
android:id="#+id/drag_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="false"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="68dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center|start"
android:paddingLeft="#dimen/activity_vertical_margin"
android:text="Near Beaver Creek"
android:textSize="20sp" />
</LinearLayout>
<ScrollView
android:id="#+id/scroll_view" <!-- Match attrs reference -->
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/transparent"
android:cacheColorHint="#android:color/white"
android:divider="#android:color/darker_gray"
android:dividerHeight="#dimen/divider_height"
android:layout_marginTop="68dp"
android:drawSelectorOnTop="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="5000dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical"/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
</com.bouy76.sportsmantracker.SlidingUpPanelLayout>
SlidingUpPanelLayout.java
View mScrollView;
int mScrollViewResId = -1;
boolean isChildHandlingTouch = false;
float mPrevMotionX;
float mPrevMotionY;
...
#Override
protected void onFinishInflate() {
super.onFinishInflate();
if (mDragViewResId != -1) {
setDragView(findViewById(mDragViewResId));
}
if (mScrollViewResId != -1) {
mScrollView = findViewById(mScrollViewResId);
}
}
...
private boolean isScrollViewUnder(int x, int y) {
if (mScrollView == null)
return false;
int[] viewLocation = new int[2];
mScrollView.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0] &&
screenX < viewLocation[0] + mScrollView.getWidth() &&
screenY >= viewLocation[1] &&
screenY < viewLocation[1] + mScrollView.getHeight();
}
...
//Removed the onInterceptTouchEvent Method
...
public boolean onTouchEvent(MotionEvent ev) {
if (!mCanSlide || !mIsSlidingEnabled) {
return super.onTouchEvent(ev);
}
mDragHelper.processTouchEvent(ev);
final int action = ev.getAction();
boolean wantTouchEvents = false;
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_UP: {
final float x = ev.getX();
final float y = ev.getY();
final float dx = x - mInitialMotionX;
final float dy = y - mInitialMotionY;
final int slop = mDragHelper.getTouchSlop();
View dragView = mDragView != null ? mDragView : mSlideableView;
if (dx * dx + dy * dy < slop * slop &&
isDragViewUnder((int) x, (int) y) &&
!isScrollViewUnder((int) x, (int) y)) {
dragView.playSoundEffect(SoundEffectConstants.CLICK);
if (!isExpanded() && !isAnchored()) {
expandPane(mAnchorPoint);
} else {
collapsePane();
}
break;
}
break;
}
}
return wantTouchEvents;
}
...
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mScrollView == null)
return super.dispatchTouchEvent(ev);
final int action = MotionEventCompat.getActionMasked(ev);
final float x = ev.getX();
final float y = ev.getY();
if (action == MotionEvent.ACTION_DOWN) {
mDragHelper.shouldInterceptTouchEvent(ev);
mInitialMotionX = mPrevMotionX = x;
mInitialMotionY = mPrevMotionY = y;
isChildHandlingTouch = false;
} else if (action == MotionEvent.ACTION_MOVE) {
float dx = x - mPrevMotionX;
float dy = y - mPrevMotionY;
mPrevMotionX = x;
mPrevMotionY = y;
if (!isScrollViewUnder((int) x, (int) y))
return this.onTouchEvent(ev);
if (dy > 0) { // DOWN
// Is the child less than fully scrolled?
// Then let the child handle it.
if (mScrollView.getScrollY() > 0) {
isChildHandlingTouch = true;
return super.dispatchTouchEvent(ev);
}
if (isChildHandlingTouch) {
// Send an 'UP' event to the child.
MotionEvent up = MotionEvent.obtain(ev);
up.setAction(MotionEvent.ACTION_UP);
super.dispatchTouchEvent(up);
up.recycle();
ev.setAction(MotionEvent.ACTION_DOWN);
}
isChildHandlingTouch = false;
return this.onTouchEvent(ev);
} else if (dy < 0) {
if (mSlideOffset > 0.0f) {
isChildHandlingTouch = false;
return this.onTouchEvent(ev);
}
if (!isChildHandlingTouch) {
mDragHelper.cancel();
ev.setAction(MotionEvent.ACTION_DOWN);
}
isChildHandlingTouch = true;
return super.dispatchTouchEvent(ev);
}
} else if ((action == MotionEvent.ACTION_CANCEL) ||
(action == MotionEvent.ACTION_UP)) {
if (!isChildHandlingTouch) {
final float dx = x - mInitialMotionX;
final float dy = y - mInitialMotionY;
final int slop = mDragHelper.getTouchSlop();
if ((mIsUsingDragViewTouchEvents) &&
(dx * dx + dy * dy < slop * slop))
return super.dispatchTouchEvent(ev);
return this.onTouchEvent(ev);
}
}
return super.dispatchTouchEvent(ev);
}
Again check this for where I'm pulling code.
https://stackoverflow.com/a/22720204/172336
Thanks
In my android app, I have a crop image. So, I programming a CropBox that extends ImageView. I want to move and resize the crop box. I use OnTouchListener, But MotionEvent.ACTION_MOVE not work. I google it but I don't catch anything.
Attr Class:
public class Attr {
public static final float CROP_BOX_START_X = 5;
public static final float CROP_BOX_START_Y = 5;
public static final float CROP_BOX_END_X = 305;
public static final float CROP_BOX_END_Y = 105;
}
CropBox Class:
public class CropBox extends ImageView {
private Paint paint = new Paint();
public CropBox(Context context) {
super(context);
}
public CropBox(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
float[][] circleXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2},
{Attr.CROP_BOX_END_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2}
};
float[][] lineXY = {
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
{Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y}
};
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1);
for(int i = 0 ; i < circleXY.length ; i++)
canvas.drawCircle(circleXY[i][0], circleXY[i][1], 5, paint);
paint.setStrokeWidth(2);
for(int i = 0 ; i < lineXY.length ; i++)
canvas.drawLine(lineXY[i][0], lineXY[i][1], lineXY[i][2], lineXY[i][3], paint);
}
}
CropTestActivity Class:
public class CropTestActivity extends Activity {
private ImageView imageView;
private CropBox cropBorder;
private RelativeLayout relativeLayout;
private RelativeLayout.LayoutParams layoutParams;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_test_layout);
imageView = (ImageView)findViewById(R.id.android_image);
cropBorder = new CropBox(this);
relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
ViewTreeObserver viewTreeObserver = imageView.getViewTreeObserver();
if(viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
layoutParams.leftMargin = imageView.getWidth() / 2 - (int)((Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2) + imageView.getLeft();
layoutParams.topMargin = imageView.getHeight() / 2 - (int)((Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2) + imageView.getTop();
}
});
}
relativeLayout.addView(cropBorder, layoutParams);
cropBorder.setOnTouchListener(new Crop());
}
}
Crop Class:
public class Crop implements OnTouchListener {
private static final int NONE = 0;
private static final int BOX_DRAG = 1;
private static final int BORDER_DRAG = 2;
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
private PointF start = new PointF();
private int mode = NONE;
private float cropBoxStartX = Attr.CROP_BOX_START_X;
private float cropBoxStartY = Attr.CROP_BOX_START_Y;
private float cropBoxEndX = Attr.CROP_BOX_END_X;
private float cropBoxEndY = Attr.CROP_BOX_END_Y;
public boolean onTouch(View view, MotionEvent event) {
CropBox cropBox = (CropBox)view;
switch(event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
if(event.getX() > cropBoxStartX && event.getX() < cropBoxEndX && event.getY() > cropBoxStartY && event.getY() < cropBoxEndY)
mode = BOX_DRAG;
else if(event.getX() == cropBoxStartX || event.getX() == cropBoxEndX || event.getY() == cropBoxStartY || event.getY() == cropBoxEndY)
mode = BORDER_DRAG;
else
mode = NONE;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if(mode == BOX_DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
}
else if(mode == BORDER_DRAG) {
}
break;
}
cropBox.setImageMatrix(matrix);
return true;
}
}
Layout XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/crop_test_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/android_image"
android:src="#drawable/android"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_gravity="center"
android:scaleType="fitXY"
android:contentDescription="#string/android_image_description" >
</ImageView>
</RelativeLayout>
Thanks for your help.
I believe that your implementation is right, the only thing that I didn't see is you setting the scale type of your CropBox to MATRIX to be able to use setImageMatrix.