I have read many posts about my problem but I haven't found a solution. I'm going crazy!
I use the following code, in the main Class, to get the pulse on the screen:
public boolean onTouchEvent(MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
case MotionEvent.ACTION_MOVE:
view.draw(canvas);
view.invalidate();
break;
}
view is:
DrawView view;
and DrawView is an inner class:
public class DrawView extends LinearLayout
and in the DrawView class I have the next two methods to draw the line:
public void draw(Canvas canvas) {
}
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.drawLine((int) 0,(int) 0,(int) x,(int) y, paint);
}
My problem is how to properly draw lines on screens with different screen densities.
I've tried with:
float d = this.getResources().getDisplayMetrics().scaledDensity;
But I still can't paint the lines correctly.
Thanks for your attention!
Related
My app has a class, MarkedLine, which extends View. An instance of this class is shown in a Fragment. I want users to be able to do the following 3 things:
Enlarge the line by doing "pinch" and "stretch" gestures
Touch any point of the line and get its coordinates
Move the line around
I have the first two working, but can't figure out the third one (the dragging).
Each MarkedLine consists of a horizontal line of boxes, some of which are coloured in. The user can zoom in by stretching, and tap a box to change its colour; I also want them to be able to move the line around the screen, because when it's zoomed in it will go off the edges of the screen.
The basic fragment layout (fragment_marked_line) is as follows (I've removed irrelevant bits, padding, margins etc.):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<packagepath.models.ToolboxButton
android:id="#+id/toolbarNext"
android:layout_width="#dimen/toolbar_icon_size"
android:layout_height="#dimen/toolbar_icon_size"
android:src="#drawable/next_line"
res:layout_constraintTop_toTopOf="parent" />
<packagepath.models.MarkedLine
android:id="#+id/markedLine"
android:layout_width="0dp"
android:layout_height="wrap_content"
res:layout_constraintStart_toStartOf="parent"
res:layout_constraintEnd_toEndOf="parent"
res:layout_constraintTop_toBottomOf="#+id/toolbarNext" />
</android.support.constraint.ConstraintLayout>
(So basically it's a button with a full-width line underneath it. The button allows the user to bring up the next line).
The Fragment code (MarkedLineFragment) is as follows (n.b. A LineSet is basically just an array of MarkedLines, with a few extra variables like when it was created, the line dimensions etc):
public class MarkedLineFragment extends Fragment {
LineSet mLineSet
MarkedLine mMarkedLine;
ToolboxButton btn_next;
int mItemNumber, mMaxItems;
public MarkedLineFragment() {}
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_marked_line, container, false);
// Get view objects
btn_next = rootView.findViewById(R.id.toolbarNext);
mMarkedLine = rootView.findViewById(R.id.markedLine);
// Initialise the button
initialise_button();
// If the LineSet has already been set,
// pass it through to the MarkedLine
if(mLineSet != null) {
mMarkedLine.setLineSet(mLineSet);
mMaxItems = mLineSet.getNum_items();
}
// Initialise at line 1
mItemNumber = 1;
mMarkedLine.setCurrentItem(mItemNumber);
// Draw the MarkedLine
drawLine();
return rootView;
}
// Initialise the button so that it moves to the next line on clicking
public void initialise_button() {
btn_next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mItemNumber == mMaxItems) return;
else mItemNumber += 1;
set_new_item_number();
}
});
}
private void set_new_item_number() {
mMarkedLine.setCurrentItem(mItemNumber);
}
public void drawChart() {
if(mMarkedLine != null) mMarkedLine.postInvalidate();
}
}
Finally, MarkedLine class (I've left out the details of how the line is drawn, because I don't think it's relevant, and it's quite long - but I can add it in if needed):
public class MarkedLine extends View {
private LineSet mLineSet;
private int currentItem;
private int numBoxes;
private float canvas_height, canvas_width;
private float box_size;
private float minX, maxX, minY, maxY;
// Scaling (pinch & zoom) variables
private float scaleFactor = 1.0f; // Current scale factor
private ScaleGestureDetector detectorScale;// Detector for gestures
private GestureDetector detectorTouch; // Detector for tap gestures
public MarkedLine(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
detectorScale = new ScaleGestureDetector(thisContext, new MarkedLine.ScaleListener());
detectorTouch = new GestureDetector(thisContext, new MarkedLine.TouchListener());
}
public void setCallback(OnBoxTouched callback) { mCallback = callback; }
public void setLineSet(LineSet lineSet) {
mLineSet = lineSet;
numBoxes = mLineSet.getNum_boxes();
invalidate();
}
public void setCurrentItem(int newItemNumber) {
currentItem = newItemNumber;
invalidate();
}
protected void onDraw(Canvas canvas) {
if (mLineSet == null) return;
// Set up canvas
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
// draw_boxes reads how many boxes make up the MarkedLine,
// calculates what size they need to be to fit on the canvas,
// and then draws them
draw_boxes();
// fill_in_line adds in the appropriate colours to the
// boxes in the line
fill_in_line();
canvas.restore();
}
// GRID EVENT FUNCTIONS - respond to User touching screen
// onTouchEvent
// User has touched the screen - trigger listeners
#Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
invalidate();
return true;
}
// LISTENERS
/*
* Respond to user touching the screen
*/
private class TouchListener extends GestureDetector.SimpleOnGestureListener {
public boolean onSingleTapUp(MotionEvent event) {
// Determine where the screen was touched
float xTouch = event.getX();
float yTouch = event.getY();
// Check that the touch was within the line; return if not
if(!touch_in_line(xTouch, yTouch)) return false;
// Figure out which Box was tapped
int xCell = getTouchedBox(xTouch);
// Now the box which was tapped is coloured in
colour_box(xCell);
return true;
}
}
/*
* Determine scale factor for zoom mode
* This can be called in View and Edit Activities
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float MIN_ZOOM = 1f; // Minimum zoom level
float MAX_ZOOM = 5f; // Maximum zoom level
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
This all works fine. The user can stretch the line to make the boxes bigger/smaller, and then tap on any of the boxes on the line to colour them in. However, I can't get the box to move around the screen when the user drags their finger on it.
I assume I need to add an onDragListener to something, but I can't figure out what. I tried having a DragListener class, similar to the ScaleListener and TouchListener classes, with an onDrag method (I just has a couple of dummy lines so I could attach a breakpoint). Then I declared an instance of that class (dragListener). I tried attaching it in the MarkedLine constructor using this.onDragListener(dragListener) but it didn't respond to dragging.
Then I attempted something similar in the Fragment, attaching it to the mMarkedLine class in onCreateView, but again it didn't respond when I tried to drag.
I've read the Android documentation, which suggested the onDragListener class, but I'm clearly doing something wrong.
In case it helps anyone else, I fixed this by adding a check for dragging in the onTouchEvent of the MarkedLine class:
#Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
// Check for drag gestures
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_UP:
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
break;
}
invalidate();
return true;
}
This is a basic code for detecting touch + drawing a custom view :
public boolean onTouchEvent(MotionEvent event) { //Basic onTouch code for scrolling along the Y axis
super.onTouchEvent(event);
if(event.getActionMasked()==MotionEvent.ACTION_DOWN){
mPrevious = event.getY();
}
if(event.getActionMasked()==MotionEvent.ACTION_MOVE){
float distance = mPrevious - event.getY();
scrollBy(0,Math.round(distance));
mPrevious = event.getY();
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Basic drawing of a circle at (200,200)
canvas.drawCircle(200f,200f,50f, defaultPaint);
//canvas.setMatrix(new Matrix());
Log.d("CANVAS", "("+canvas.getMatrix().toString()+")");
}
I'm working with a custom view, I'm trying to implement user scrolling functionality using scrollBy, but it looks like I'll have to create the logic myself.
I still want to understand how this function works. When I print log the canvas matrix in the last line, it correctly displays the new coordinates, and onDraw is called every time the touch moves.
But if I uncomment canvas.setMatrix, suprisingly nothing changes functionality-wise. The only difference now is that the console log shows that the canvas matrix is always equal to identity mx, even if it's correctly being scrolled. Why? How can scrollBy completely overwrite canvas drawing?
I am kind of new to the onDraw so i don't know how to go around this problem.
I want to draw a image at this location (just so i can try the rest myself). These locations are off the ontap and * the drawables width and height. This code is in a class that extends photoview.
The problem is this resource is not being displayed, i also tried to use on a drawcirlce example that also didnt display. I have also attached that below.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap markerBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.markerformap);
canvas.drawBitmap(markerBitmap, 2000, 5000, new Paint());
}
Draw circle example
canvas.drawCircle(2000, 5000,300, new Paint());
The onTouch Listener
#Override
public boolean onTouch(View v, MotionEvent event) {
if(y != event.getY() && x != event.getX()){
x = event.getX();
y = event.getY();
return true;
}
return false;
}
I want to build an app that has an imageview and it simply follows the finger touch. In other words, the imageview follows the touch of the finger. I have partially achieved this, in the sense that the imageview is following the touch of the finger but the problem is that it is at some offset. The imageview is about an inch down the actual touch.
Below is my code:
public class MainActivity extends AppCompatActivity {
ImageView img;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img = (ImageView)findViewById(R.id.img1);
img.setImageResource(R.drawable.ic_remove_circle_black_24dp);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
img.setX(event.getX());
img.setY(event.getY());
return false;
}
}
What could be the reason behind the offset?
Edit: Solution
Following code does what I need:
public class MainActivity extends AppCompatActivity {
ImageView img;
int[] viewCoords = new int[2];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img = (ImageView)findViewById(R.id.img1);
img.setImageResource(R.drawable.ic_remove_circle_black_24dp);
}
#Override
public boolean onTouchEvent(MotionEvent event){
img.getLocationOnScreen(viewCoords);
img.setX(event.getX()-(viewCoords[0]-img.getX()));
img.setY(event.getY()-(viewCoords[1]-img.getY()));
return true;
}
}
Thanks to Doomsknight for the direction.
From what I can see, you are using the X and Y coordinates of the touch event of the Screen.
And then drawing this coordinates on the X and Y of the ImageView.
This will cause the offset you are seeing, as they do not match up, as the imageview probably isnt at the very top of your screen.
What I think you are trying to do is get the X and Y, of the actual image.
You will therefore need to offset the coordinates accordingly.
There is a good example of this already of getting the offset, and removing it: see link below.
https://stackoverflow.com/a/11312423/940834
You can get the top left corner of your view as follows:
int[] viewCoords = new int[2];
imageView.getLocationOnScreen(viewCoords);
From this and the touch
coordinates you can calculate the point inside the ImageView:
int touchX = (int) event.getX();
int touchY = (int) event.getY();
int imageX = touchX - viewCoords[0]; // viewCoords[0] is the X coordinate
int imageY = touchY - viewCoords[1]; // viewCoords[1] is the y coordinate
I want move a bitmap in the screen, but i can do it only horizontally, because if I do it vertically, the scroll of the view start and the movement of the bitmap disappears. I have
public boolean onTouchEvent(MotionEvent event)
method where in case MOVE I change the X and Y of the bitmap. Then in onDraw() method I paint the bitmap. The scroll are in xml. Inside of it are a layout and inside a View.
I would like when I touch, in case DOWN, if I touch the bitmap, disable the scroll, but I is in other place no.
that is the resume of the code
public class Table extends ScrollView{
private static Integer srcX = 0, srcY = 0;
public Table(Context context) {
super(context);
setWillNotDraw(false);
setVerticalScrollBarEnabled(true);
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for(int i=1; i<= Paint_Table.num_players; i++)
canvas.drawBitmap(bitmap, X, Y, null);
}
#SuppressLint("ClickableViewAccessibility") #Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
if (youTouchBitmap) {
srcX = x;
srcY = y;
}
break;
case MotionEvent.ACTION_MOVE:
if (srcX !=0 && srcY != 0) {
X=x;
Y=y;
invalidate();
}
break;
}
return true;
}
}
Can someone help me?
Thanks and regards.
since i dont see code ive find some link might help you:
How to disable and enable the scrolling on android ScrollView?
in the link you can see in the answers a code that is custom scroll view its work on flag.
you can choose when the scroll will work ot not.
hope i helpd :)