I have some custom view with onTouchEvent where I'm setting mX and mY .
mx=event.getX();
my=event.getY();
this is for getting the drawing path of touch event - working great, and I can
see the coordinates in my text view which is defined in the main activity.
I want to use this 2 attributes in my Main activity layout
for setting the background color
for example
if (mx > 1000 && my > 100 ){
set color
}
but it seems that mx and my are not getting any values
what i tried is
custom_view paintview = (custom_view) findViewById(R.id.custum_view_id)
float mx =paintview.getmx();
float my =paintview.getmy();
maybe i didn't defined the getmx correctly in custom view ?
public float getmx(){
return mx;
}
i'm new in Java and android so be easy with me :)
adding MainActiviy.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Drawing - custom view
drawView = (Drawing) findViewById(R.id.drawing);
findCoordinates();
TextView Coordinates = (TextView) findViewById(R.id.CNcoordinates);
//Coordinates - message with coordinates
paintView.setTextView(Coordinates);
}
private Drawing drawView;
public void findCoordinates() {
Drawing paintView = (Drawing) findViewById(R.id.drawing);
float xX = drawView.getmX();
float yY = drawView.getmY();
int nColor = Color.BLUE;
View buttonmove1 = findViewById(R.id.layoutMove);
if (Math.abs(xX) > 1000 && Math.abs(yY) > 1000) {
buttonmove1.setBackgroundColor(nColor);
}
}
the coordinates come from:
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mX = event.getX();
mY = event.getY();
View buttonmove = findViewById(R.id.drawing);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(mX, mY);
if(myCoordinates!=null){
myCoordinates.setText(":" + mX + " , " + ":" + mY);
}
break;
public void setTextView(TextView tv){
myCoordinates = tv;
}
Use interface or callback after finishing your touchEvent. you have to re-call your
findCoordinates()
so it will update your UI respect to new values.
i succeed to use OnViewTouchListener as a callback:
adding the method in custom view:
private OnViewTouchListener OnViewTouchListener;
public interface OnViewTouchListener {
public void onViewTouched (float x, float y, boolean touched);
}
public void setOnViewTouchListener (OnViewTouchListener listener){
OnViewTouchListener = listener;
}
Related
I want to circular reveal animation for recycler view items. I put the following code in the adapter class but it doesn't work. please help me.
public void anim(final CustomViewHolder customViewHolder) {
// get the center for the clipping circle
int centerX = (customViewHolder.imageView.getLeft() + customViewHolder.imageView.getRight()) / 2;
int centerY = (customViewHolder.imageView.getTop() + customViewHolder.imageView.getBottom()) / 2;
int startRadius = 0;
// get the final radius for the clipping circle
int endRadius = Math.max(customViewHolder.imageView.getWidth(), customViewHolder.imageView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
anim = ViewAnimationUtils.createCircularReveal(customViewHolder.rv,
centerX, centerY, startRadius, endRadius);
anim.setDuration(1000);
// make the view invisible when the animation is done
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
customViewHolder.rv.setVisibility(View.VISIBLE);
anim.start();
}
}
I'm trying to draw a scrollable timeline. I implement singleLine custom view for drawing a line between circle ImageView's of recycler view:
public class SingleLine extends View {
WeakReference<Context> ctx;
Paint paint;
PointF pointA, pointB;
private int height_custom;
public SingleLine(Context context) {
super(context);
ctx = new WeakReference<>(context);
init();
}
public SingleLine(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
ctx = new WeakReference<>(context);
init();
}
public void init() {
paint = new Paint();
pointA = new PointF();
pointB = new PointF();
paint.setStrokeWidth(10);
paint.setColor(ctx.get().getResources().getColor(R.color.green));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(pointA.x, pointA.y, pointB.x, pointB.y, paint);
invalidate();
}
public PointF getPointA() {return pointA;}
public void setPointA(PointF pointA) {this.pointA = pointA;}
public PointF getPointB() {return pointB;}
public void setPointB(PointF pointB) {this.pointB = pointB;}
}
And in onBindViewHolder() method of my adapter, I use an interface for measuring x/y coordinates of first and last visible view of recyclerview:
#Override
public void onBindViewHolder(#NonNull final TaskViewHolder holder, final int position) {
Tasks tasks = datalist_tasks.get(position);
String hour = "" + tasks.getHour() + ":" + tasks.getMinute();
holder.tv_clock.setText(hour);
holder.tv_title.setText(tasks.getTitle());
holder.tv_comment.setText(tasks.getComment());
holder.itemView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
holder.itemView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
holder.itemView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
if (iOnMeasureCustomView != null)
iOnMeasureCustomView.onMeasure(holder.iv_status, position);
}
});
}
And I implement that interface in my activity like below:
LinearLayoutManager layoutManager1 = new LinearLayoutManager(_C.get(), RecyclerView.VERTICAL, false);
rv_tasks.setLayoutManager(layoutManager1);
tasksAdapter.setiOnMeasureCustomView(new IOnMeasureCustomView() {
#Override
public void onMeasure(ImageView iv_status, int position) {
int[] screen = new int[2];
iv_status.getLocationOnScreen(screen);
int width = (int) ((iv_status.getWidth() / 2) + convertDpToPixel(8));
if (position == layoutManager1.findFirstCompletelyVisibleItemPosition()) {
pointA.set(iv_status.getX() + width, iv_status.getY() + convertDpToPixel(8));
}
else if (position == layoutManager1.findLastCompletelyVisibleItemPosition()) {
pointB.set(iv_status.getX() + width, screen[1] - convertDpToPixel(40));
singleLine.setPointA(pointA);
singleLine.setPointB(pointB);
singleLine.invalidate();
}
}
});
And also I implement addOnScrollListener() for my recyclerview and write this code snippet (I don't know that if this implementation is correct or no):
rv_tasks.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
PointF a = singleLine.getPointA();
PointF b = singleLine.getPointB();
if (dy > 0) {
a.y -= dy;
b.y -= dy;
singleLine.setPointA(a);
singleLine.setPointB(b);
singleLine.invalidate();
} else {
a.y += Math.abs(dy);
b.y += Math.abs(dy);
singleLine.setPointA(a);
singleLine.setPointB(b);
singleLine.invalidate();
}
}
});
My problem is when I scroll in activity, like the gif below, my singleLine is something like cutting off imageView and the end x/y of line become the x/y of end of the screen.
Questios
How can I set x/y of the line to prevent this?
And also I need some help with drawing this line with animation. How can I draw this line with animation that starts when the activity started?
Hello,
I am trying to implement dynamic map (View) into my application and I need to make it's movement behave like Google Map has. So It has to do both, scroll and fling. (I have reasons for not using the actual Google Maps).
At First time, I handled dragging and flinging of map by it's Scroll. But it jittered, because void View.setScrollX(int) can't have parameter of float and so it isn't smooth enough.
At Second, I tried Translation for moving of the map, but even when dragging alone was working and fling alone was working, together it did strange behaviour on fling.
My question is: How to make Scroll and Fling work together well? I will be glad of every advice or snipped of code :)
This is the second solution:
ImageView img;
String msg = "My LOG";
float firstPosX = 0;
float firstPosY = 0;
int touchCount;
FlingAnimation flingAnimX;
FlingAnimation flingAnimY;
GestureDetector gestureDetector;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img = (ImageView) findViewById(R.id.imageView);
gestureDetector = new GestureDetector(getApplicationContext(), mGestureListener);
img.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
touchCount = event.getPointerCount();
//Stops Fling, so on the new touch it dont continue
if (flingAnimX != null) {
flingAnimX.cancel();
}
if (flingAnimY != null) {
flingAnimY.cancel();
}
if (touchCount == 1) {
float deltaPosX = 0;
float deltaPosY = 0;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
firstPosX = event.getX();
firstPosY = event.getY();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
deltaPosX = -firstPosX + event.getX();
deltaPosY = -firstPosY + event.getY();
img.setTranslationX(deltaPosX + img.getTranslationX());
img.setTranslationY(deltaPosY + img.getTranslationY());
}
}
gestureDetector.onTouchEvent(event);
return true;
}
});
}
GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
if (touchCount == 1) {
flingAnimX = new FlingAnimation(img, DynamicAnimation.TRANSLATION_X)
.setStartVelocity(velocityX);
flingAnimX.start();
flingAnimY = new FlingAnimation(img, DynamicAnimation.TRANSLATION_Y)
.setStartVelocity(velocityY);
flingAnimY.start();
}
return true;
}
};
I'm trying to change an ImageView's height with only one finger movement (upside or downside). The code below changes height with two fingers (like pinching or reverse). How can I modify this code to do what I want? Thanks.
public class MainActivity extends Activity {
private ImageView iv;
private Matrix matrix = new Matrix();
private float scale = 1f;
private ScaleGestureDetector SGD;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = iv=(ImageView)findViewById(R.id.imageView);
SGD = new ScaleGestureDetector(this, new ScaleListener());
}
public boolean onTouchEvent(MotionEvent ev) {
SGD.onTouchEvent(ev);
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
#Override
public boolean onScale(ScaleGestureDetector detector) {
scale *= detector.getScaleFactor();
scale = Math.max(0.1f, Math.min(scale, 5.0f));
matrix.setScale(1, scale);
iv.setImageMatrix(matrix);
return true;
}
}
}
Use GestureDetector for scroll:
#Bind(R.id.container)
ViewGroup container;
#Bind(R.id.image)
ImageView image;
private GestureDetectorCompat detector;
private float yscale = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
ButterKnife.bind(this);
image.setScaleType(ImageView.ScaleType.MATRIX);
detector = new GestureDetectorCompat(this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Matrix m = new Matrix();
yscale += distanceY / container.getHeight();
m.setScale(1, yscale, 0, 0);
image.setImageMatrix(m);
return true;
}
});
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
} detector.onTouchEvent(event);
}
change
public boolean onTouchEvent(MotionEvent ev) {
SGD.onTouchEvent(ev);
return true;
}
to
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN, case MotionEvent.ACTION_UP:
SGD.onTouchEvent(ev);
break;
return true;
}
This code snipet should help you to detect up and down
private VelocityTracker mVelocityTracker = null;
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Best practice to use VelocityTrackerCompat where possible.
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
I'm trying to implement a personal way of undo/redo in a finger paint-like app.
I have in synthesis three objects: the Main class (named ScorePadActivity), the relative Main Layout (with buttons, menus, etc, as well as a View object where I create my drawings), and a third object named ArrayList where i'm writing the undo/redo code.
The problem is, when I press the undo button nothing happens, but if I draw anything again "one-time" and press undo, the screen is updated. If I draw many times, to see any changes happen on screen I have to press the undo button the same number of times I have drawn.
Seems like (as in title) when I add a bitmap to the array list the last element is duplicated in previous indexes, and for some strange reason, everytime I press the Undo Button, the system is ok for one time, but starts to duplicate until the next undo.
The index increase is verified with a series of System.out.println inserted in code.
Now when I draw something on screen, the array list is updated with the code inserted after the invocation of touchup(); method in motionEvent
touch_up(); }
this.arrayClass.incrementArray(mBitmap);
mPath.rewind();
invalidate();
and in ArrayList activity;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
undoArray.add(_size, _mBitmap);
}
(All Logs removed for clear reading)
The undo button in ScorePadActivity calls the undo method in View activity:
Button undobtn= (Button)findViewById(R.id.undo);
undobtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mView.undo();
}
});
in View activity:
public void undo() {
this.mBitmap= arrayClass.undo();
mCanvas = new Canvas(mBitmap);
mPath.rewind();
invalidate();
}
that calls the relative undo method in ArrayList activity:
public Bitmap undo() {
// TODO Auto-generated method stub
_size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
return mBitmap and invalidate:
Due to my bad English I have made a scheme to make the problem more clear:
I have tried with HashMap, with a simple array, I have tried to change mPath.rewind(); with reset();, new Path(); etc but nothing.
Why?
Sorry for the complex answer, i want give you a great thanks in advance.
Best regards
Edit
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:id="#+id/buttonlayout"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
some other layouts nested an buttons to form a upper toolbar
</LinearLayout>
<RelativeLayout
android:id="#+id/viewlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/buttonlayout"
android:background="#drawable/desk_wood" >
<com.example.android.touchexample.MyView
android:id="#+id/viewout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
</RelativeLayout>
This the Main Activity ScorePadActivity
public class ScorePadActivity extends Activity {
MyView mView;
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mView = (MyView)findViewById(R.id.viewout);
Button undobtn= (Button)findViewById(R.id.undo);
undobtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mView.undo();
}
});
This is the View Activity:
public class MyView extends View {
MyView myView;
Context context;
final ArrayClass arrayClass= new ArrayClass();
private Bitmap mBitmap;
private Bitmap savedBmp;
private static Canvas mCanvas;
private static Path mPath;
private static Paint mPaint;
/*
* some other variables here
*/
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(bSize);
mPaint.setAlpha(255);
mPath = new Path();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap((int) bmWidth, (int) bmHeight,Bitmap. Config.ARGB_8888);}
/*
* here add a blank bitmap at the start of the array at index 0
*/
arrayClass.incrementArray(mBitmap);
mCanvas = new Canvas(mBitmap);
}
public void onDraw(Canvas canvas) {
canvas.save();
bx= (((width/mScaleFactor)-width)/2)+center;
by= ((height/mScaleFactor)-height)/2;
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.drawBitmap(penta, bx, by, null);
mCanvas.drawPath(mPath, mPaint);
canvas.drawBitmap(mBitmap, bx, by, null);
lastmPosX=mPosX;
lastmPosY=mPosY;
lastmScaleFactor=mScaleFactor;
canvas.restore();
}
private void touch_start(float x, float y) {
x=((x/mScaleFactor)-bx)-(mPosX/mScaleFactor);
y=((y/mScaleFactor)-by)-(mPosY/mScaleFactor);
mPath.rewind();
mPath.moveTo(x, y);
move=false;
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
x=((x/mScaleFactor)-bx)-(mPosX/mScaleFactor);
y=((y/mScaleFactor)-by)-(mPosY/mScaleFactor);
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
move=true;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// mPath.rewind();
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
x = ev.getX();
y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
// Here update the arraylist in the ArrayList activity
this.arrayClass.incrementArray(mBitmap);
mPath.rewind();
invalidate();
break;}
return true;
}
/*
* more methods here
*/ switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
public void undo() {
// Here recall the last mBitmap from Arraylist activity
this.mBitmap= arrayClass.undo();
mCanvas = new Canvas(mBitmap);
mPath.rewind();
invalidate();
}
}
and this is my ArrayList activity:
public class ArrayClass {
ArrayList<Bitmap> undoArray =new ArrayList<Bitmap>();
private int _size;
private Bitmap _mBitmap;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
// undoArray.add(_size, _mBitmap);
undoArray.add(_size, Bitmap.createBitmap(_mBitmap));
}
public Bitmap undo() {
// TODO Auto-generated method stub
_size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
public Bitmap redo() {
// TODO
return null;
}
}
Thanks again
Here the solution, edit the ArrayClass in this way:
public class ArrayClass {
ArrayList<Bitmap> undoArray =new ArrayList<Bitmap>();
private int _size;
private Bitmap _mBitmap;
public void incrementArray(Bitmap mBitmap) {
this._mBitmap=mBitmap;
_size=undoArray.size();
// undoArray.add(_size, _mBitmap); replace with:
undoArray.add(_size, Bitmap.createBitmap(_mBitmap));
}
public Bitmap undo() {
// TODO Auto-generated method stub
_ size=undoArray.size();
if (_size>1) {
undoArray.remove(_size-1);
_size=undoArray.size();
_mBitmap = ((Bitmap) undoArray.get(_size-1)).copy(Bitmap.Config.ARGB_8888,true);
}
return _mBitmap;
}
public Bitmap redo() {
// TODO
return null;
}
}