I'm new to Android development.
I have a class DrawView that extends View, it's like a simple paint app
public class DrawView extends View implements OnTouchListener {
Bitmap bitmap;
Canvas bitmapCanvas;
int color;
// Position of finger down
float pX, pY;
// Position of finger up
float mX, mY;
// Create new path
Path path = new Path();
// Is view initialized?!
boolean isInitialized;
// Create new paint
Paint paint = new Paint();
int begX, begY, endX, endY = 0;
//DrawView constructor
public DrawView(Context context) {
// Initialize new view
super(context);
setFocusable(false);
setFocusableInTouchMode(false);
this.setOnTouchListener(this);
requestLayout();
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
// There is no bitmap yet
isInitialized = false;
}
//Initialize bitmap and canvas
private void init() {
bitmap = Bitmap.createBitmap(getWidth(), getHeight()/2, Bitmap.Config.RGB_565);
bitmap.setPixel(72, 72, Color.BLACK);
// Create new canvas and set bitmap
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
// ... set canvas background color
bitmapCanvas.drawColor(Color.WHITE);
// We're done with initialization
isInitialized = true;
}
//Reset canvas
public void reset(){
bitmapCanvas.drawColor(Color.WHITE);
}
//Handle event 'onDraw'
#Override
public void onDraw(Canvas canvas) {
// Check if initialized
if (!isInitialized)
init();
// Draw bitmap!
canvas.drawBitmap(bitmap, 0, 0, paint);
}
//Handle event 'onTouch'
public boolean onTouch(View view, MotionEvent event) {
// Check event type
switch (event.getAction()) {
// Finger down
case MotionEvent.ACTION_DOWN:
paint.setColor(Color.BLACK);
paint.setStrokeWidth(7f);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAntiAlias(true);
// Get current position
pX = event.getX();
pY = event.getY();
// Set beginning of path to (posX,posY)
path.moveTo(pX, pY);
begX= (int) pX;
begY = (int) pY;
bitmapCanvas.drawPoint(pX, pY, paint);
break;
// Finger moves
case MotionEvent.ACTION_MOVE:
mX = event.getX();
mY = event.getY();
// Set position of end of path
path.lineTo(mX, mY);
endX = (int) mX;
endY = (int) mY;
// Draw path
bitmapCanvas.drawPath(path, paint);
// Invalidate canvas (redraw the view)
invalidate();
break;
// Finger up
case MotionEvent.ACTION_UP:
mX = event.getX();
mY = event.getY();
if (mY == pY && mX == pX){
bitmapCanvas.drawPoint(pX, pY, paint);
invalidate();
}
path.reset();
break;
}
return true;
}
}
In my default layout, I want it to contains DrawView and other elements (button, textview ...)
How can I split "default layout" to contains DrawView with these elements?
Thanks
edit:
Where should I put this code to make DrawView work correctly
public class Draw extends Activity {
DrawView drawView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
drawView = new DrawView(this);
setContentView(drawView);
drawView.requestFocus();
}
}
Add your view with it's full name to your layout.For example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...>
<TextView.../>
<my.package.MyCustomView
android:id="#+id/my_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button.../>
</LinearLayout>
Here MyCustomView is name of java class that extends View and my.package is name of package that contains that class.You can see that you can add LayoutParams to it,as I added layout_height or layout_width.
Edit:
If you want to use your custom view in XML layout,you have to add at least this constructor to your class:
public MyCustomView(Context context, AttributeSet attrs){
super(context, attrs);
...
}
I don't know how you want actually but you can use any layout as per your requirement here is the simple layout which your your view and another views in split screen
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_weight="1">
<TextView /> <!--your textview-->
<TextView /> <!--your textview-->
<TextView /> <!--your textview-->
</LinearLayout>
<com.androidapp.DrawView android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
This will split the screen in equal size for your view and another view with nested LinearLayout
If you want to display your content with wrap size then just remove the weight if you display at top of your DrawView and use wrap_content in height or if you display at bottom than give them weight in 0.2 or o.3 whatever but in float
Edit
Define both constructor
public DrawView(Context context) {
// Initialize new view
super(context);
initObject();
}
public MyCustomView(Context context, AttributeSet attrs){
super(context, attrs);
initObject();
}
private void initObject(){
setFocusable(false);
setFocusableInTouchMode(false);
this.setOnTouchListener(this);
requestLayout();
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
// There is no bitmap yet
isInitialized = false;
}
Related
I'm trying to move a custom view that is in my main activity with a single touch input, however the x/y coordinates for the event are offset due to the action bar.
GIF of problem
I'm trying to find a way to negate the size of the action bar to the y coordinate but nothing seems to be working. I've subtracting the difference in the parent view and my custom view's size from the y coordinate getRootView().getHeight() - getHeight() but the value is incorrect.
Can anyone point me in the right direction?
The custom view:
public class SampleView extends View {
private Paint paint;
private Path path = new Path();
public SampleView(Context context) {
super(context);
init();
}
public SampleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getRawX();
final int y = (int) event.getRawY();
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
path.moveTo(x, y);
break;
}
case MotionEvent.ACTION_MOVE: {
path.lineTo(x, y);
break;
}
}
invalidate();
return true;
}
}
I haven't touched my MainActivity, but have added in the XML for activity_main my custom view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.caseyweed.sample.MainActivity">
<com.caseyweed.sample.SampleView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Use getX() and getY() instead of getRawX() and getRawY() if you want coordinates relative to the view instead of the device screen coordinates.
I am using canvas to make a simple drawing app. I am using onTouchEvent to handle touch event. I am facing a problem on vertical axis drawn point is differentfrom touched position. the vertical separation in between touched position and drawn position increases as I move upward. To make my problem more clear i am attaching screenshot of my app.
Blue line shows actual touched position and red drawn position.
Here is my code
Mainactivity
public class MainActivity extends AppCompatActivity{
Path mPath;
Canvas canvas;
Paint paint;
float pointX;
float pointY;
int height;
RelativeLayout layout;
int layoutHeight;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
layout=(RelativeLayout)findViewById(R.id.main_layout);
DisplayMetrics dp=getResources().getDisplayMetrics();
WindowManager windowManager=getWindowManager();
height=windowManager.getDefaultDisplay().getHeight();
int width =windowManager.getDefaultDisplay().getWidth();
Bitmap bg = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
layout.setBackground(new BitmapDrawable(bg));
canvas=new Canvas(bg);
paint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
mPath=new Path();
canvas.drawPath(mPath,paint);
canvas.drawBitmap(bg,0,0,paint);
}
public void clearCanvas(View v) {
mPath.reset();
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
layout.invalidate();
}
//override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
pointX = event.getX();
pointY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.v("TAG"," action down x:"+pointX+" y:"+pointY);
mPath.moveTo(pointX,pointY);
break;
case MotionEvent.ACTION_MOVE:
Log.v("TAG"," actionmove x:"+pointX+" y:"+pointY);
mPath.lineTo(pointX,pointY);
canvas.drawPath(mPath,paint);
break;
case MotionEvent.ACTION_UP:
break;
}
layout.invalidate();
return true;
}
}
xml layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.vikash.mydrawingapp.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="clear"
android:onClick="clearCanvas"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Use: event.getRawX()
Returns the original raw X coordinate of this event. For touch events on the screen, this is the original location of the event on the screen, before it had been adjusted for the containing window and views.
UPD:
I just tried to repeat your app and all works correctly
activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.Test
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Test.java
public class Test extends View {
private Path path = new Path();
private Paint paint = new Paint();
{
paint.setColor(Color.GREEN);
paint.setStrokeWidth(4);
paint.setStyle(Paint.Style.STROKE);
}
public Test(Context context) {
super(context);
}
public Test(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Test(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Test(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(event.getX(), event.getY());
break;
}
invalidate();
return true;
}
}
Your problem is that your Canvas X-Axis has the same position of the Screen X-Axis, but your Canvas Y-Axis is offset from Screen Y-Axis (The "MyDrawingApp" bar sets that offset with its height).
What is happening is that when you "getY()" it returns the position of the touch taking as reference the entire screen not your canvas.
One solution is to get the position of the canvas on the screen and minus it to the "getY()" value. Doing this you will get a touch inside of your canvas.
I hope you will understand me, I am not English.
Greetings
I am working on a drawing app. And I already create a draw view that allow user to draw on the view.
The problem is , when I draw on it , it exceed the area of the image (please refer to the picture, the yellow line is exceeed the actual photo area), how can I stick the canvas size to the actual image ?
And how the imageview be zoomable? That means when user click on pen button, it draw, when user click again then it become zoom function.
Thanks. The zoom function can be tackle later and the problem of exceed area need to fix first
Here is the custom draw view in xml
<com.example.tool.DrawView
android:id="#+id/draw"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Here is the java
public class DrawView extends ImageView {
private int color = Color.BLACK;
private float width = 4f;
private List<Holder> holderList = new ArrayList<Holder>();
private class Holder {
Path path;
Paint paint;
Holder(int color, float width) {
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
}
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();
}
private void init() {
holderList.add(new Holder(color, width));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Holder holder : holderList) {
canvas.drawPath(holder.path, holder.paint);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
holderList.add(new Holder(color,width));
holderList.get(holderList.size() - 1).path.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
holderList.get(holderList.size() - 1).path.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
public void resetPaths() {
for (Holder holder : holderList) {
holder.path.reset();
}
invalidate();
}
public void setBrushColor(int color) {
this.color = color;
}
public void setWidth(float width) {
this.width = width;
}
}
And I call it by:
DrawView pic = findViewById(R.id.draw);
pic.setImageBitmap(bitmap);
Thanks a lot for helping
Calculate the displayed image size first and then clamp the eventX and eventY to the bound of the displayed image. Code as below:
int imageViewH=imageView.getMeasuredHeight();//height of imageView
int imageViewW =imageView.getMeasuredWidth();//width of imageView
int drawableH =imageView.getDrawable().getIntrinsicHeight();//original height of underlying image
int drawableW=imageView.getDrawable().getIntrinsicWidth();//original width of underlying image
int displayH, displayW; // the shown height and width of the picture.
int leftX, rightX, topY, bottomY; // the shown edges of the picture.
if (imageViewH/drawableH <= imageViewW/drawableW){
displayW = drawableW*imageViewH/drawableH;//rescaled width of image within ImageView
displayH = imageViewH;
leftX = (imageViewW - displayW)/2; // left edge of the displayed image.
rightX = leftX + displayW; // right edge of the displayed image.
topY = 0; // top edg
bottomY = displayH; // bottom edge.
}else{
displayH = drawableH*imageViewW/drawableH;//rescaled height of image within ImageView
displayW = imageViewW;
leftX = 0; // left edge of the displayed image.
rightX = displayW; // right edge of the displayed image.
topY = (imageViewH - displayH)/2; // top edg
bottomY = topY + displayH; // bottom edge.
}
//TODO: clamp the eventX and eventY to the bound of leftX, rightX, topY, bottomY
Note: the code should be used only after your ImageView has its measured height and a drawable.
You can set a width of your DrawView programmatically to ImageView's width, if in ImageView's properties there's a true attribute adjustViewBounds.
I'm doing a simple drum set app where the users can click the button to make the corresponding sound and draw a circle on the canvas at a random location. For the canvas, I created a custom view called CanvasView, then I implements that CanvasView into the MainActivity, so I can control that View each time the buttons are clicked with OnClickListener in the MainActivity.
However, I don't know how to actually control (draw a circle on) the CanvasView using the OnClickListener. I need help on this part
This is my code for the MainActivity class:
public class MainActivity extends Activity {
//setup variables for soundpool
.
.
.
//implements CanvasView
CanvasView mCanvasView;
//set up soundID for each sound
.
.
.
//setup buttons
Button mKick;
.
.
.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//set up soundpools
.
.
.
//example, the kick drum button
//kick drum
mKick = (Button) findViewById(R.id.kick);
mKick.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
soundPool.play(kick, leftVolume, rightVolume, priority, no_loop, normal_playback_rate);
// mCanvasView.draw(CanvasView.mCanvas);
}
});
This is my code for the CanvasView
public class CanvasView extends View {
public Paint mPaint;
public static Canvas mCanvas;
//constructor
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
}
//what I want to draw is here
protected void onDraw(Canvas canvas) {
mCanvas = canvas;
super.onDraw(mCanvas);
canvas.drawColor(Color.GRAY);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
canvas.drawCircle(30, 30, radius, mPaint);
This is my layout XML for the MainActivity
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/GridLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:columnCount="3"
android:orientation="horizontal"
android:rowCount="5"
tools:context=".GridXMLActivity" >
<Button
android:id="#+id/kick"
android:layout_width="78dp"
android:layout_height="wrap_content"
android:layout_column="1"
android:layout_gravity="center_horizontal|fill_vertical"
android:layout_row="3"
android:text="Kick" />
<com.example.virtualdrumset.CanvasView
android:id="#+id/canvasView1"
android:layout_width="143dp"
android:layout_height="169dp"
android:layout_column="1"
android:layout_gravity="left|top"
android:layout_row="0" />
I deeply apologize for any inconvenience or mistakes. This is my first time asking question.
You shouldn't call onDraw function of the canvas directly from the activity. Instead make a public method in the canvas view and write your logic there and call invalidate at the end. This calls onDraw method and contents are drawn on the screen again according to your new logic.
If you want a circle to be drawn at random locations on screen each time button is clicked you can do something like this
public class CanvasView extends View {
public Paint mPaint;
public static Canvas mCanvas;
private int mPivotX = 0;
private int mPivotY = 0;
private int radius = 60;
//constructor
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
}
public void drawCircle() {
int minX = radius * 2;
int maxX = getWidth() - (radius *2 );
int minY = radius * 2;
int maxY = getHeight() - (radius *2 );
//Generate random numbers for x and y locations of the circle on screen
Random random = new Random();
mPivotX = random.nextInt(maxX - minX + 1) + minX;
mPivotY = random.nextInt(maxY - minY + 1) + minY;
//important. Refreshes the view by calling onDraw function
invalidate();
}
//what I want to draw is here
protected void onDraw(Canvas canvas) {
mCanvas = canvas;
super.onDraw(mCanvas);
canvas.drawColor(Color.GRAY);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
canvas.drawCircle(mPivotX, mPivotY, radius, mPaint);
}
}
And then onButtonClick
mKick.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
soundPool.play(kick, leftVolume, rightVolume, priority, no_loop, normal_playback_rate);
mCanvasView.drawCircle()
}
Hope it helps!
This is my ImageView in androidmanifest file.
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="+id/imageview1"></ImageView>
I drawn a circle on imageview on touch but the circle is drawn at left corner top.
This is my code:
ImageView myimage=(ImageView)findViewById(R.id.imageview1);
myimage.setImageResource(R.drawable.penguins);
BitmapDrawable bmpdraw=(BitmapDrawable)myimage.getDrawable();
Bitmap bmp=bmpdraw.getCopy().config();
Canvas m_canvas=new Canvas(bmp);
public void onTouch(View v,MotionEvent event){
int x=event.getX();
int y=event.getY();
canvas.drawCircle(x,y,10,paint);
myimage.setBitmap(bmp);
}
I came to know that canvas position is different when compared with imageview.
Try to following code .
my_canvas.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rl_main"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</RelativeLayout>
MyCanvas.java
public class MyCanvas extends Activity {
private RelativeLayout rl_Main;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms_read);
rl_Main = (RelativeLayout) findViewById(R.id.rl_main);
rl_Main.addView(new MyView(this));
}
class MyView extends View{
Paint paint = new Paint();
Point point = new Point();
public MyView(Context context) {
super(context);
paint.setColor(Color.RED);
paint.setStrokeWidth(15);
paint.setStyle(Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
Bitmap b=BitmapFactory.decodeResource(getResources(), R.drawable.images);
canvas.drawBitmap(b, 0, 0, paint);
canvas.drawCircle(point.x, point.y, 100, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
point.x = event.getX();
point.y = event.getY();
}
invalidate();
return true;
}
}
class Point {
float x, y;
}
}