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):
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
How to Get Complete Address from Latitude and Longitude
Changing API Level Android Studio
How to Restart Activity in Android
Can an Android Toast Be Longer Than Toast.Length_Long
"Android.View.Windowmanager$Badtokenexception: Unable to Add Window" on Buider.Show()
What Does @Hide Mean in the Android Source Code
Building and Running App Via Gradle and Android Studio Is Slower Than Via Eclipse
Imageview in Circular Through Xml
How to Find Out If the Gps of an Android Device Is Enabled
Is the Way the Firebase Database Quickstart Handles Counts Secure
How to Create an Object of an Activity in Other Class
"Debug Certificate Expired" Error in Eclipse Android Plugins
How to Change Progress Bar'S Progress Color in Android
How to Make an Imageview With Rounded Corners