In my Android project, I encountered some difficulties in the drawing code, so I made up a MCVE below to demonstrate the problem I have.
By running this example, and perform a touch in the screen, you can see two circles coming up.
You can see that the red one is positioned accurately and consistently around the point of touch.
For the green one, you can see that its position is somehow random on repeat touching of the screen.
The red circle is created using an ImageView with the source as ball.xml. For the green one, it is done by Canvas.drawCircle().
So:
How do I correct the code to make the red circle and green circle
appear at exactly the same spot on a touch, regardless of the resolution and dpi of the screen?
Also, btw, the 500X1000 dimension (Bitmap.createBitmap(500, 1000))is just set arbitrarily, it should be according to the screen size of the device.
MainActivity
package com.prime.testdraw;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
ImageView ivPhoto;
FrameLayout fl;
ImageView ivBall;
private Canvas myCV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivPhoto = ((ImageView) findViewById(R.id.ivPhoto));
fl = ((FrameLayout) findViewById(R.id.fl));
ivBall = ((ImageView) findViewById(R.id.ivBall));
myCV = initDraw(ivPhoto);
ivPhoto.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
myCV = initDraw(ivPhoto);
ivBall.setX(motionEvent.getX() - ivBall.getWidth()/2);
ivBall.setY(motionEvent.getY() - ivBall.getHeight()/2);
Paint p = new Paint();
p.setStrokeWidth(3);
p.setColor(Color.GREEN);
p.setTextSize(100);
int radius = 40;
p.setStyle(Paint.Style.STROKE);
myCV.drawCircle(motionEvent.getX() - radius, motionEvent.getY() - radius, radius, p);
ivPhoto.invalidate();
return false;
}
});
}
private Canvas initDraw(ImageView imageView) {
Bitmap bmp = Bitmap.createBitmap(500, 1000, Bitmap.Config.RGB_565);
Canvas cv = new Canvas(bmp);
imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp));
imageView.invalidate();
return cv;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:id="#+id/fl">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ivPhoto"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<ImageView
android:layout_width="52dp"
android:layout_height="52dp"
android:id="#+id/ivBall"
android:layout_gravity="left|top"
android:src="#drawable/ball" />
</FrameLayout>
ball.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
<stroke
android:width="4dp"
android:color="#FF0000" />
<gradient android:startColor="#FFFF0000" android:endColor="#80FF0000"
android:angle="270"/>
</shape>
Canvas.drawCircle works with these parameters :
cx float: The x-coordinate of the center of the cirle to be drawn
cy float: The y-coordinate of the center of the cirle to be drawn
radius float: The radius of the cirle to be drawn
paint Paint: The paint used to draw the circle
Therefore instead of substracting the radius when calling it, simply call drawCircle directly :
myCV.drawCircle(motionEvent.getX(), motionEvent.getY(), radius, p);
Source : Android Canvas doc
Related
Good day all.
I have an image in an imageView and I am using ObjectAnimator to move and rotate the image onscreen. I have a number of x-y coordinates, which are relative to the image inside the imageView. How can I access any given such x-y point despite moving and rotating the image onscreen? Any help would be appreciated.
I am coding in Java.
Paul
MainActivity
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import static android.widget.Toast.makeText;
public class MainActivity<theMap> extends AppCompatActivity {
// private static final int LENGTH_SHORT = ;
private ImageView imageView;
#SuppressLint("WrongConstant")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.mymap);
imageView.animate()
.scaleX(1)
.scaleY(1);
}
public void onButtonClickRotate(View v) {
int[] WayPointsX = {433,503,605,668,820,919,1060,1168,1258,1371,1412};
int[] WayPointsY = {463,478,421,397,367,363,376,400,438,510,543};
ObjectAnimator animX = ObjectAnimator.ofFloat(imageView, "translationX", -750f);
ObjectAnimator animY = ObjectAnimator.ofFloat(imageView, "translationY", -450f);
// ObjectAnimator.ofFloat(imageView, "pivotX", 1500f).start();
// ObjectAnimator.ofFloat(imageView, "pivotY", 0F).start();
// ObjectAnimator animR = ObjectAnimator.ofFloat(imageView, "rotation", 0F, 360F);
AnimatorSet s = new AnimatorSet();
s.play(animX).with(animY);
// s.play(animR).after(animY);
s.setDuration(3000);
s.start();
}
}
activity_main
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
<Button
android:id="#+id/btnRunRotate"
android:layout_width="100dp"
android:layout_height="50dp"
android:onClick="onButtonClickRotate"
android:text="#string/rotate" />
<ImageView
android:id="#+id/mymap"
android:layout_width="500dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:background="#drawable/themap"/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginBottom="10dip"
android:layout_gravity="center"
android:background="#drawable/shopper" />
</FrameLayout>
Now the two Arrays WayPointsX and WayPointsY are coordinates on the image which I want to step-wise move to. As they stand the coordinate values are from a Javascript code and I am trying to figure out what the values should be in Java bearing in mind that during the animation the onscreen graphic will also be rotating about set pivot points.So the WayPoint Array values are currently just parked there and my code does not use these numbers currently. I achieved this animation with ease in the Javascript build.
The AnimX and AnimY vales of
-750f and -450f are actual points which the java animations take the graphic to. I need to know how to get the values for the required points on the graphic.
I have also realised that I am trying to go to a point on the graphic to an accuracy of a few pixels but find this to be way off and dependent on the emulator I use or the actual device I connect to.
Hope this makes sense.
I have 3 custom views placed vertically in a LinearLayout, they are used to display different dynamic info, so they're supposed be invalidated and redrawn at different time. But I found the view invalidation is out of usual expectation, that is: if you invalidate the top view,all 3 views are invalidated at the same time, if you invalidate the middle view, the middle and bottom views are invalidated, the top one is not, if you invalidate the bottom view, only the bottom view itself is invalidated, this is what I want, so what happened with the first 2 cases ? I searched and got similar questions like:
https://stackoverflow.com/questions/26192491/invalidate-one-view-force-other-views-invalidate-too-how-separating-that
Android Invalidate() only single view
but it seems no exact answer. I post my code here, any comment is appreciated.
TestView.java
package com.vrb.myview;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
public class TestView extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onTest(View view){
MyView1 mv1 = (MyView1)findViewById(R.id.mv1);
MyView1 mv2 = (MyView1)findViewById(R.id.mv2);
MyView1 mv3 = (MyView1)findViewById(R.id.mv3);
mv1.invalidate(); // all 3 views are invalidated
// mv2.invalidate(); // mv2 and mv3 are invalidated
// mv3.invalidate(); // only mv3 is invalidated,this is what I want
}
}
MyView1.java
package com.vrb.myview;
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class MyView1 extends View {
Rect rc=null;
Paint p=null;
Random r;
public MyView1(Context ctx){
super(ctx);
rc = new Rect();
p = new Paint();
r = new Random();
}
public MyView1(Context ctx, AttributeSet set){
super(ctx, set);
rc = new Rect();
p = new Paint();
r = new Random();
}
public void onDraw(Canvas canvas){
if(canvas.getClipBounds(rc)){
Log.d("MyView1","id="+getId()+" Rect: "+rc.left+","+rc.top+","+rc.right+","+rc.bottom);
p.setColor(Color.argb(0xff, Math.abs(r.nextInt())%255, Math.abs(r.nextInt())%255, Math.abs(r.nextInt())%255));
canvas.drawRect(rc, p);
}else{
Log.d("MyView1","id="+getId()+" Rect=null");
}
}
}
main.xml
<LinearLayout 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"
android:id="#+id/root"
android:orientation="vertical"
tools:context="com.vrb.myview.TestView" >
<com.vrb.myview.MyView1
android:layout_width="match_parent"
android:layout_height="100px"
android:id="#+id/mv1" />
<com.vrb.myview.MyView1
android:layout_width="match_parent"
android:layout_height="100px"
android:id="#+id/mv2" />
<com.vrb.myview.MyView1
android:layout_width="match_parent"
android:layout_height="100px"
android:id="#+id/mv3" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Invalidate"
android:onClick="onTest"
android:id="#+id/btn" />
</LinearLayout>
You shouldn't rely on the count or the time of the calls to onDraw() for the internal state of your View. Move the p.setColor() call to a separate public method, and call invalidate() at the end of it. For example:
public class MyView1 extends View {
...
public void changePaint() {
p.setColor(Color.argb(0xff, Math.abs(r.nextInt()) % 255, Math.abs(r.nextInt()) % 255, Math.abs(r.nextInt()) % 255));
invalidate();
}
}
Then in your onTest() method:
public void onTest(View view) {
MyView1 mv1 = (MyView1)findViewById(R.id.mv1);
...
mv1.changePaint();
...
}
I have a simple linear layout. I am adding button dynaically through code one below other with some space in between. I need to draw a vertical line between those two buttons (Vertical arrow headed line in specific).
Could you please let me know how to draw a vertical line from buttom of button1 to top of button2.
I used DrawLine() to draw a line but its getting drawn below button2 with some offset.
Here is my code:
import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
public class SampleMethodActivity extends Activity {
Button b,b1;
public int width,height,bottom;
LinearLayout ll;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_method);
ll = (LinearLayout) findViewById(R.id.my_linear_layout);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(0, 12, 0, 40);
b = new Button(this);
b.setText("This is a sample text");
b.setLayoutParams(params);
b.setGravity(Gravity.CENTER);
ll.addView(b);
b1 = new Button(this);
b1.setText("This is a sample text to chck the width and height of a button1 and need to check how long it gets stretched and to check the width");
b1.setLayoutParams(params);
b1.setGravity(Gravity.CENTER);
ll.addView(b1);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
width = b.getWidth();
height = b.getHeight();
bottom = b.getBottom();
DrawView dv = new DrawView(this,width/2,bottom);
ll.addView(dv);
}
}
Below is DrawView class:
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();
int x,y;
public DrawView(Context context,int x,int y) {
super(context);
this.x=x;
this.y=y;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(3);
System.out.println("X: "+x);
canvas.drawLine(x, y, x, y+400, paint);
}
}
Drawing a Line with View
If you only need a straight horizontal or vertical line, then the easiest way may be to just use a View in your xml layout file. You would do something like this:
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#android:color/black" />
Set first button gravity to left and other to right and in between them add a view in between them and set it's gravity to center.
Second Way
Add button (weight 45) view(10) button2(weight45) and you are done.
and you can also create an xml and inflate it to LinearLayout.
For vertical line width to 1dp and for horizontal line height to 1dp
Add this to colors.xml
<color name="streat_line">#D0D5D8</color>
apply this where you want to create a line
<TextView
android:id="#+id/TextView"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:background="#color/streat_line" >
I'm trying to center text in a canvas in a TextView subclass. It looks fine in Eclipse, but not in the device:
Eclipse:
Device:
There's a reason I want to center the text in this way. Which I'm not explaining here (and is also not in the code), since it doesn't belong to the problem.
I'm already using dips and sps.
The code:
package com.example.test2;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
public class CenterTest extends TextView {
private Paint paint;
private Paint paint2;
private Rect bounds;
public CenterTest(Context context) {
super(context);
}
public CenterTest(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint2 = new Paint();
bounds = new Rect();
}
public void draw(Canvas canvas) {
canvas.save();
int width = getWidth();
int height = getHeight();
//draw background
paint2.setColor(Color.rgb(0, 0, 0));
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, width, height, paint2);
//measure text
paint.setTextSize(getTextSize());
paint.getTextBounds(getText().toString(), 0, getText().toString().length(), bounds);
int textWidth = bounds.width();
int textHeight = bounds.height();
//center before drawing text
canvas.translate((width / 2) - (textWidth / 2), 0);
canvas.translate(0, (height / 2) - (textHeight / 2));
//let TextView.onDraw() do the rest
super.onDraw(canvas);
canvas.restore();
}
}
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff"
>
<com.example.test2.CenterTest
android:layout_width="100dip"
android:layout_height="30dip"
android:text="label"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="12sp"
/>
</RelativeLayout>
Maybe the reason is somewhere in TextView.onDraw() but that's more than 200 lines of code and I don't have time now... and not even sure if it's there.
Thanks in advance!
try to use android:gravity="center"
<com.example.test2.CenterTest
android:layout_width="100dip"
android:layout_height="30dip"
android:text="label"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="12sp"
android:gravity="center"
/>
I'm using the shapedrawable example word for word (nearly) and can't seem to call a shapedrawable class in xml. The only extra step stated by the documentation was to override the View(Context, AttributeSet), which I think I did. The docs I'm referring to are here: http://developer.android.com/guide/topics/graphics/2d-graphics.html Here is my code.
AndroidTest.java
package com.android.test;
import android.app.Activity;
import android.os.Bundle;
public class AndroidTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
ShapeSquare.java
package com.android.test;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.view.View;
public class ShapeSquare extends View {
private ShapeDrawable mDrawable;
public ShapeSquare(Context context, AttributeSet attrs) {
super(context, attrs);
int x = 10;
int y = 10;
int width = 300;
int height = 50;
mDrawable = new ShapeDrawable(new OvalShape());
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.test.shapedrawable.ShapeSquare
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
The error is a force quit error and I can't figure out where the problem lies. The shape properties will be dictated by user input (eventually), so the shape needs to be created in a class as opposed to all xml.
Figured out the problem here. I had to remove "shapedrawable" from:
<com.android.test.shapedrawable.ShapeSquare
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Apparently, that was just the location of the demo. I thought it was referencing the class somehow.