How to Draw a Line in Android

How to draw a line in android

This one draws 2 lines which form a cross on the top left of the screen:


DrawView.java

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

public class DrawView extends View {
Paint paint = new Paint();

private void init() {
paint.setColor(Color.BLACK);
}

public DrawView(Context context) {
super(context);
init();
}

public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

@Override
public void onDraw(Canvas canvas) {
canvas.drawLine(0, 0, 20, 20, paint);
canvas.drawLine(20, 0, 0, 20, paint);
}

}

The activity to start it:

StartDraw.java

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;

public class StartDraw extends Activity {
DrawView drawView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

drawView = new DrawView(this);
drawView.setBackgroundColor(Color.WHITE);
setContentView(drawView);

}
}

android.graphics draw a line from one View pointing to another View

For drawing lines between views better if all of it lays on same parent layout. For the conditions of the question (Second Button is exactly to the right of First Button) you can use custom layout like that:

public class ArrowLayout extends RelativeLayout {

public static final String PROPERTY_X = "PROPERTY_X";
public static final String PROPERTY_Y = "PROPERTY_Y";

private final static double ARROW_ANGLE = Math.PI / 6;
private final static double ARROW_SIZE = 50;

private Paint mPaint;

private boolean mDrawArrow = false;
private Point mPointFrom = new Point(); // current (during animation) arrow start point
private Point mPointTo = new Point(); // current (during animation) arrow end point

public ArrowLayout(Context context) {
super(context);
init();
}

public ArrowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

public ArrowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}

private void init() {
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
}

@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
if (mDrawArrow) {
drawArrowLines(mPointFrom, mPointTo, canvas);
}
canvas.restore();
}

private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();

pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;

return pointFrom;
}


private Point calcPointTo(Rect fromViewBounds, Rect toViewBounds) {
Point pointTo = new Point();

pointTo.x = toViewBounds.left;
pointTo.y = toViewBounds.top + (toViewBounds.bottom - toViewBounds.top) / 2;

return pointTo;
}


private void drawArrowLines(Point pointFrom, Point pointTo, Canvas canvas) {
canvas.drawLine(pointFrom.x, pointFrom.y, pointTo.x, pointTo.y, mPaint);

double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);

int arrowX, arrowY;

arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle + ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle + ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);

arrowX = (int) (pointTo.x - ARROW_SIZE * Math.cos(angle - ARROW_ANGLE));
arrowY = (int) (pointTo.y - ARROW_SIZE * Math.sin(angle - ARROW_ANGLE));
canvas.drawLine(pointTo.x, pointTo.y, arrowX, arrowY, mPaint);
}

public void animateArrows(int duration) {
mDrawArrow = true;

View fromView = getChildAt(0);
View toView = getChildAt(1);

// find from and to views bounds
Rect fromViewBounds = new Rect();
fromView.getDrawingRect(fromViewBounds);
offsetDescendantRectToMyCoords(fromView, fromViewBounds);

Rect toViewBounds = new Rect();
toView.getDrawingRect(toViewBounds);
offsetDescendantRectToMyCoords(toView, toViewBounds);

// calculate arrow sbegin and end points
Point pointFrom = calcPointFrom(fromViewBounds, toViewBounds);
Point pointTo = calcPointTo(fromViewBounds, toViewBounds);

ValueAnimator arrowAnimator = createArrowAnimator(pointFrom, pointTo, duration);
arrowAnimator.start();
}

private ValueAnimator createArrowAnimator(Point pointFrom, Point pointTo, int duration) {

final double angle = Math.atan2(pointTo.y - pointFrom.y, pointTo.x - pointFrom.x);

mPointFrom.x = pointFrom.x;
mPointFrom.y = pointFrom.y;

int firstX = (int) (pointFrom.x + ARROW_SIZE * Math.cos(angle));
int firstY = (int) (pointFrom.y + ARROW_SIZE * Math.sin(angle));

PropertyValuesHolder propertyX = PropertyValuesHolder.ofInt(PROPERTY_X, firstX, pointTo.x);
PropertyValuesHolder propertyY = PropertyValuesHolder.ofInt(PROPERTY_Y, firstY, pointTo.y);

ValueAnimator animator = new ValueAnimator();
animator.setValues(propertyX, propertyY);
animator.setDuration(duration);
// set other interpolator (if needed) here:
animator.setInterpolator(new AccelerateDecelerateInterpolator());

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mPointTo.x = (int) valueAnimator.getAnimatedValue(PROPERTY_X);
mPointTo.y = (int) valueAnimator.getAnimatedValue(PROPERTY_Y);

invalidate();
}
});

return animator;
}
}

with .xml layout like:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_main"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<{YOUR_PACKAGE_NAME}.ArrowLayout
android:id="@+id/arrow_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/first_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="First Button"/>

<Button
android:id="@+id/second_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Second Button"/>

</{YOUR_PACKAGE_NAME}.ArrowLayout>

</RelativeLayout>

and MainActivity.java like:

public class MainActivity extends AppCompatActivity {

private ArrowLayout mArrowLayout;
private Button mFirstButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mArrowLayout = (ArrowLayout) findViewById(R.id.arrow_layout);

mFirstButton = (Button) findViewById(R.id.first_button);
mFirstButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mArrowLayout.animateArrows(1000);
}
});
}
}

you got something like that (on First Button click):

[Arrow to View animated

For other cases ( Second Button is exactly to the left (or above, or below) or more complex above-right/below-left etc. of First Button) you should modify part for calculating arrow begin and end points:

private Point calcPointFrom(Rect fromViewBounds, Rect toViewBounds) {
Point pointFrom = new Point();

// Second Button above
// ----------+----------
// | |
// Second Button tho the left + First Button + Second Button tho the right
// | |
// ----------+----------
// Second Button below
//
// + - is arrow start point position

if (toViewBounds to the right of fromViewBounds){
pointFrom.x = fromViewBounds.right;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if (toViewBounds to the left of fromViewBounds) {
pointFrom.x = fromViewBounds.left;
pointFrom.y = fromViewBounds.top + (fromViewBounds.bottom - fromViewBounds.top) / 2;
} else if () {
...
}

return pointFrom;
}

How to Draw Line in Android by Dragging

After further examining your code, I believe I have achieved what you'd like.

We're going to go to DrawingView and define a getter for drawCanvas, so we can access our canvas outside of the DrawingView class.

Next we're going to head to Basketball and do the following:

float startX;
float startY;
public boolean onTouch(View view, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getRawX();
startY = event.getRawY();
...
break;

case MotionEvent.ACTION_UP:
Paint paint = new Paint(); //set this as a field in drawView with another getter to avoid garbage collection penalties
paint.setStrokeWidth(15f);
paint.setColor(Color.BLACK);
drawView.getCanvas().drawLine(startX, startY, event.getRawX(), event.getRawY(), paint);
drawView.invalidate();
break;
}
}

What this does: when you pick up the chip it will save the starting coordinates, and when you drop the chip it will draw a line in your drawView canvas from start to end.

You can even draw the lines as a continuous Path so the lines always touch, but that is outside the context of this answer.

Draw a line in jetpack compose

You can use

Divider Composable

method for Horizontal line like below.

Divider(color = Color.Blue, thickness = 1.dp)

Example :

@Composable
fun drawLine(){
MaterialTheme {

VerticalScroller{
Column(modifier = Spacing(16.dp), mainAxisSize = LayoutSize.Expand) {

(0..3).forEachIndexed { index, i ->
Text(
text = "Draw Line !",
style = TextStyle(color = Color.DarkGray, fontSize = 22.sp)
)

Divider(color = Color.Blue, thickness = 2.dp)

}
}
}

}

}

Android draw a Horizontal line between views

It will draw Silver gray colored Line between TextView & ListView

<TextView
android:id="@+id/textView1"
style="@style/behindMenuItemLabel1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:text="FaceBook Feeds" />

<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#c0c0c0"/>

<ListView
android:id="@+id/list1"
android:layout_width="350dp"
android:layout_height="50dp" />

Draw a Line with a Java class Android

draw a line then use this code to draw line simply

public class MainActivity extends Activity {

DrawLine drawLine;

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

drawLine = new DrawLine(this);

drawLine.setBackgroundColor(Color.CYAN);

setContentView(drawLine);
}

class DrawLine extends View {
Paint paint = new Paint();
public DrawLine(Context context) {
super(context);
paint.setColor(Color.BLACK);
}

@Override
public void onDraw(Canvas canvas) {
canvas.drawLine(50, 100, 600, 600, paint);
canvas.drawLine(50, 550, 770, 0, paint);
}

}
}

How to draw lines on an ImageView using canvas which are unaffected by zooming or scaling?

The correct approach is to render the lines dynamically using a View canvas, and not into the bitmap itself.

The reason why your implementation fails, is because if you render the grid directly into the bitmap, then you are effectively changing the bitmap pixels, so logically if you zoom the bitmap then the grid pixels will also zoom, as everything bitmap + grid have become the same single image.

A solution can be to use any ready-made zoom image view widget. You'll find lots in GitHub such as for example:

ZoomImageView

Then you have 2 options, to extend the widget to create your own subclass, or to perform the implementation directly into the widget code. Is up to you.

What you need to do in any of both cases, is to override its onDraw callback, and perform the grid drawing right after the super.onDraw(canvas) call.
No need to create new bitmaps or new canvas instances. Use the method's canvas to perform the drawing.

@Override
protected void onDraw(final Canvas canvas) {

super.onDraw(canvas);

// Render the grid

final int slotWidth = this.getWidth() / 3;
final int slotHeight = this.getHeight() / 3;

this.paint.setColor(this.gridColor);

// Horizontal lines
canvas.drawLine(0, slotHeight, this.getWidth(), slotHeight, this.paint);
canvas.drawLine(0, (slotHeight * 2), this.getWidth(), (slotHeight * 2), this.paint);

// Vertical lines
canvas.drawLine(slotWidth, 0, slotWidth, this.getHeight(), this.paint);
canvas.drawLine((slotWidth * 2), 0, (slotWidth * 2), this.getHeight(), this.paint);
}

As a side note, avoid creating new objects inside the onDraw method, such as Paint instances, etc. as the onDraw can be called several times in a short period. Lint should warn you anyway about this. Never create new object instances in methods such as onDraw, onMeasure, etc., for such scenarios always precache/reuse them.

A simpler alternative, is to extend a View and create a new dedicated grid view widget, unrelated to the actual Zoom Image View. Perform the same, so, also override the onDraw method as the above example, and render the grid as shown. Then place this new View into your layout design, exactly on top of your Zoom Image View control, ensuring both controls have the same metrics.



Related Topics



Leave a reply



Submit