I am writing a program that draws a circle at a random location on the phone screen. What it is supposed to do is when the circle is touched, it removes that circle and moves it to another random location.
Currently, when the screen is tapped the old circle is removed, and redrawn at another random location on the screen. It is very close, but I was wondering how I get over this hurdle.
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructors
public Circle(Context context) {
super(context);
init();
}
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Circle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
// might be used later for some data collection
}
// gets random number,,
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
#Override
public boolean onTouchEvent(MotionEvent event) {
generateRandom();
invalidate();
return super.onTouchEvent(event);
}
}
Change the code like this ,
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isInsideCircle(event.getX(), event.getY())) {
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return (dxPow + dyPow) < radPow || (dxPow + dyPow == radPow);
}
Related
I need to implement something like SeekBar. I created my class heir from View. In which I paint an oval on canvas. When touched, this oval goes up and down. I can not correctly calculate the position for the point (thumb).
enter image description here
here is my code, but it has already been redone many times and stopped working:
public class BalanceView extends View {
private Paint ovalPaint;
private Paint thumbPaint;
private float ovalBottom = 0f;
private Bitmap thumb;
private RectF oval1;
//params
private int maxValue = 18;
private float ovalPaintWidth = 2.0f;
private float ovalHeight = 60f;
//end params
private float touchX = 0f;
private float touchY = 0f;
private float mUnreachedRadius;
public BalanceView(Context context) {
super(context);
init(context, null);
}
public BalanceView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public BalanceView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context ctx, #Nullable AttributeSet attrs){
if (attrs != null){
}
if (ovalPaint == null){
ovalPaint = new Paint();
ovalPaint.setAntiAlias(true);
ovalPaint.setStrokeWidth(ovalPaintWidth);
ovalPaint.setStyle(Paint.Style.STROKE);
Shader shader = new SweepGradient(10, 10, new int[]{R.color.white, R.color.black, R.color.white}, null);
ovalPaint.setShader(shader);
}
if (thumbPaint == null){
thumbPaint = new Paint();
}
thumb = BitmapFactory.decodeResource(getResources(), R.drawable.move_point);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//refershPosition();
}
#Override
protected void onDraw(Canvas canvas) {
paintOval(ovalPaint, canvas);
float cos = computeCos(touchX, touchY);
float y = calcYLocationInWheel(cos);
Log.d("balance", "cos: " + cos + " y: " + y + " touchX: " + touchX + " touchY: " + touchY + "mUnreachedRadius: " + mUnreachedRadius) ;
canvas.drawBitmap(thumb, touchX, y, thumbPaint);
super.onDraw(canvas);
}
private void paintOval(Paint paint, #NonNull Canvas canvas){
if (paint != null) {
int right = getWidth() - 30;
int left = (getWidth() - right) / 2;
float top = ovalBottom - ovalHeight;
oval1 = new RectF(left, top, right + left, ovalBottom);
canvas.drawOval(oval1, paint);
}
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getY() >= getTop() && event.getY() <= getBottom()) {
touchY = event.getY();
}
if (event.getX() <= oval1.right - thumb.getWidth() && event.getX() >= oval1.left) {
touchX = event.getX();
}
ovalBottom = touchY;
invalidate();
return super.dispatchTouchEvent(event);
}
private float computeCos(float x, float y) {
float width = x - oval1.width() / 2;
float height = y - oval1.height() / 2;
float slope = (float) Math.sqrt(width * width + height * height);
return height / slope;
}
private float calcYLocationInWheel(double cos) {
return oval1.bottom * (float) cos;
}
}
I try so:
private float getY(float a, float b, float x){
// return (float) (Math.pow(b, 2d) * ((Math.pow(a, 2d) - Math.pow(x, 2d)) / Math.pow(a, 2d)));
return (float)
((Math.pow(b, 2d) / Math.pow(a, 2d)) * (Math.sqrt(b * b * (a * a - x * x))));
}
But the data is wrong.
I'm working on a project that requires me to draw a circle at a random location on the screen when the user touches the screen. When the touch event occurs it also needs to remove the last circle I drew, so that there can only be one circle on the screen at one time.
I have a class Circle, that I use to create a circle:
public class Circle extends View {
private final float x;
private final float y;
private final int r;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public Circle(Context context, float x, float y, int r) {
super(context);
mPaint.setColor(0x000000);
this.x = x;
this.y = y;
this.r = r;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
}
This is the activity from which a new Circle is created. It will also monitor touch events. Right now it is drawing a circle in the upper left hand corner of the screen, but that code will need to be moved into the onTouch method. I will likely calculate the random number using Random.
public class FingerTappingActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_finger_tapping);
LinearLayout circle = (LinearLayout) findViewById(R.id.lt);
View circleView = new Circle(this, 300, 300, 150);
circleView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
circle.addView(circleView);
circle.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
}
I have never worked with touch events before such as these and have no idea how to go about this. Some tips would be greatly appreciated.
Do some thing like this ,
public class DrawCircles extends View {
private float x = 100;
private float y = 100;
private int r = 50;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
public DrawCircles(Context context) {
super(context);
init();
}
public DrawCircles(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DrawCircles(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
mPaint.setColor(0x000000);
// logic for random call here;
}
Random random = new Random();
void generateRandom() {
int minRadius = 100;
int w = getWidth();
int h = getHeight();
this.x = random.nextInt(w);
this.y = random.nextInt(h);
this.r = minRadius + random.nextInt(100);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
generateRandom();
invalidate();
return super.onTouchEvent(event);
}
}
This view will draw random circles inside the view . make some adjustments for generating x & y coordinates . add this view to xml then it will work.
I am trying to create a custom view that has a Circle and in it, I have to have sections in run time as shown in the image below. I tried a lot of stuff in onDraw method but got no luck. I even tried https://github.com/donvigo/CustomProgressControls . Basically, I want to give a number of sections and then in each section I can select colors as per my need.
I am looking for ProgressBar that should have gap/space as shown in the image; in between circles. Say if I have given 5 sections, 3 of which should be "full", it should color the first 3 in red, and the other 2 in green, for example.
To draw I am doing like:
private void initExternalCirclePainter() {
internalCirclePaint = new Paint();
internalCirclePaint.setAntiAlias(true);
internalCirclePaint.setStrokeWidth(internalStrokeWidth);
internalCirclePaint.setColor(color);
internalCirclePaint.setStyle(Paint.Style.STROKE);
internalCirclePaint.setPathEffect(new DashPathEffect(new float[]{dashWith, dashSpace}, dashSpace));
}
I might be a little late to the party, but I actually wrote a custom component that has 2 rings that look quite similar to what you're trying to achieve. You can just remove the outer ring easily. The image of what I got in the end:
Here's the class:
public class RoundedSectionProgressBar extends View {
// The amount of degrees that we wanna reserve for the divider between 2 sections
private static final float DIVIDER_ANGLE = 7;
public static final float DEGREES_IN_CIRCLE = 360;
public static final int PADDING = 18;
public static final int PADDING2 = 12;
protected final Paint paint = new Paint();
protected final Paint waitingPaint = new Paint();
protected final Paint backgroundPaint = new Paint();
private int totalSections = 5;
private int fullSections = 2;
private int waiting = 3; // The outer ring. You can omit this
private RectF rect = new RectF();
public RoundedSectionProgressBar(Context context) {
super(context);
init(context, null);
}
public RoundedSectionProgressBar(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RoundedSectionProgressBar(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
// Can come from attrs if need be?
int strokeWidth = 3;
setupPaint(context, strokeWidth, paint, R.color.filled_color_inner_ring);
setupPaint(context, strokeWidth, waitingPaint, R.color.empty_color_inner_ring);
setupPaint(context, strokeWidth, backgroundPaint, R.color.filled_color_outer_ring);
}
private void setupPaint(Context context, int strokeWidth, Paint backgroundPaint, int colorRes) {
backgroundPaint.setStrokeCap(Paint.Cap.SQUARE);
backgroundPaint.setColor(context.getResources().getColor(colorRes));
backgroundPaint.setAntiAlias(true);
backgroundPaint.setStrokeWidth(strokeWidth);
backgroundPaint.setStyle(Paint.Style.STROKE);
}
public int getTotalSections() {
return totalSections;
}
public void setTotalSections(int totalSections) {
this.totalSections = totalSections;
invalidate();
}
public int getFullSections() {
return fullSections;
}
public void setNumberOfSections(int fullSections, int totalSections, int waiting) {
this.fullSections = fullSections;
this.totalSections = totalSections;
this.waiting = waiting;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rect.set(getLeft() + PADDING, getTop() + PADDING, getRight() - PADDING, getBottom() - PADDING);
float angleOfSection = (DEGREES_IN_CIRCLE / totalSections) - DIVIDER_ANGLE;
// Drawing the inner ring
for (int i = 0; i < totalSections; i++) {
// -90 because it doesn't start at the top, so rotate by -90
// divider_angle/2 especially in 2 sections, it's visibly rotated by Divider angle, so we split this between last and first
float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
if (i < fullSections) {
canvas.drawArc(rect, startAngle, angleOfSection, false, paint);
} else {
canvas.drawArc(rect, startAngle, angleOfSection, false, backgroundPaint);
}
}
// Drawing the outer ring
rect.set(getLeft() + PADDING2, getTop() + PADDING2, getRight() - PADDING2, getBottom() - PADDING2);
for (int i = 0; i < waiting; i++) {
float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
canvas.drawArc(rect, startAngle, angleOfSection, false, waitingPaint);
}
}
}
Notice that this code won't give you the outer ring's 'empty' slots, since we decided against them in the end. The inner circle will have both the empty and filled slots. The whole class can be reused, and it's responsible just for the 2 rings that are drawn, the 6/6, +3 and the red circle are parts of another view.
The most important piece of the code is the onDraw method. It contains the logic for drawing the arcs in the for loop, as well as the logic for calculating the angles and adding spaces between them. Everything is rotated by -90 degrees, because I needed it to start at the top, rather than on the right, as it is the 0-degree angle in Android. It's not that complex, and you can modify it to fit your needs better should you need to.
I find it easier to do math for drawArc(operating on angle values based on number of sections) rather than computing the arc length.
Here's a quick idea, with a lot of hard-coded properties, but you should be able to get the idea:
public class MyStrokeCircleView extends View {
private Paint mPaint;
private RectF mRect;
private int mPadding;
private int mSections;
private int mFullArcSliceLength;
private int mColorArcLineLength;
private int mArcSectionGap;
public MyStrokeCircleView(Context context) {
super(context);
init(null, 0);
}
public MyStrokeCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MyStrokeCircleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
mPaint = new Paint();
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
mPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));
mPadding = 5;
mRect = new RectF(mPadding, mPadding, mPadding, mPadding);
mSections = 4;
mFullArcSliceLength = 360 / mSections;
mArcSectionGap = mFullArcSliceLength / 10;
mColorArcLineLength = mFullArcSliceLength - 2 * mArcSectionGap;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mRect.right = getWidth() - mPadding;
mRect.bottom = getHeight() - mPadding;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mSections; i++) {
canvas.drawArc(mRect, i * mFullArcSliceLength + mArcSectionGap, mColorArcLineLength, false, mPaint);
}
}
}
I'm trying to display Text in RectShape which uses ShapeDrawable to display in Canvas.
I'm able to display RectShape in this code, but looking for how to display the String on that!
public class CustomDrawableView extends View {
private ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
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);
}
}
or
public class ShapeDrawableTest extends View {
ShapeDrawable shapes = new ShapeDrawable();
public ShapeDrawableTest(Context context) {
super(context);
}
public ShapeDrawableTest(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
shapes.draw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX(); // Use getX(int) for multi-finger
// gestures
int y = (int) event.getY();
makeShapeDrawable(x, y);
invalidate();
return (true); // Handled touch event
} else {
return (false); // Did not handle touch event
}
}
private void makeShapeDrawable(int x, int y) {
int maxWidth = getWidth() / 10;
int maxHeight = getHeight() / 10;
Shape shape;
shape = new RectShape();
shapes = new ShapeDrawable(shape);
int width = RandomUtils.randomInt(maxWidth) + 5;
int height = RandomUtils.randomInt(maxHeight) + 5;
shapes.setBounds(x - width / 2, y - height / 2, x + width / 2, y
+ height / 2);
shapes.getPaint().setColor(Color.BLUE);
}
}
What I tried is:
public class CustomDrawableView extends View {
public ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
}
public CustomDrawableView(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);
final String s = "Hello";
Paint p = new Paint();
Rect bounds = new Rect();
bounds.set(x, y, x + width, y + height);
p.setTextSize(20);
p.setColor(Color.YELLOW);
p.getTextBounds(s, 0, s.length(), bounds);
mDrawable.getPaint().getTextBounds(s, 0, s.length(), bounds);
mDrawable.setBounds(x, y, x + width, y + height);
}
// public CustomDrawableView(Context context, AttributeSet attrs, int defStyle) {
// super(context, attrs, defStyle);
//
// }
public void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}
Screenshot:
The best option for me is to create a custom TextDrawable that handles properly how to display a piece of text. Then in your CustomDrawableView in your onDraw(Canvas c) method you can call it to display the text and the oval.
Take a look at this answer I wrote recently as it contains how to do it properly.
I am using GestureDetector to identify the touch.I have two Relative layoutsof same layout size.I am trying to draw an image A on one layout and move an image B(created using onDraw) on another layout.While I move my image B I am using GestureDetector to identify the touch.When I move my image B, image A(view) onDraw() method is called multiple times. Please help me to avoid calling onDraw() multiple times.Here is my code
public class TouchExampleActivity extends Activity {
RelativeLayout r1;
RelativeLayout r2;
Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = TouchExampleActivity.this;
r1 = (RelativeLayout) findViewById(R.id.r1);
r2 = (RelativeLayout) findViewById(R.id.r2);
TouchExampleView view = new TouchExampleView(this);
DrawCircle drawCircle = new DrawCircle(context);
r1.addView(view);
r2.addView(drawCircle);
}
class DrawCircle extends View {
Paint p;
public DrawCircle(Context context) {
super(context);
// TODO Auto-generated constructor stub
p = new Paint();
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Log.v("Touch", "Called Draw Circle");
p.setColor(Color.BLUE);
canvas.drawCircle(60, 50, 50, p);
}
}
}
TouchExampleView.java
public class TouchExampleView extends View {
private Drawable mIcon;
private float mPosX;
private float mPosY;
static boolean fStartDrag = false;
static boolean fDrag = false;
private float ypos = 40;
private float xpos = 30;
public static float sx, sy;
private VersionedGestureDetector mDetector;
private float mScaleFactor = 1.f;
public TouchExampleView(Context context) {
this(context, null, 0);
}
public TouchExampleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchExampleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources().getDrawable(R.drawable.icon);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());
mDetector = VersionedGestureDetector.newInstance(context, new GestureCallback());
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mDetector.onTouchEvent(ev);
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
canvas.save();
canvas.translate(mPosX, 0);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.drawRect(0, 0, 30, 30, paint);
canvas.restore();
}
private class GestureCallback implements VersionedGestureDetector.OnGestureListener {
public void onDrag(float dx, float dy) {
if ((dx == -1) && (dy == -1)) {
fStartDrag = true;
fDrag = false;
}
else if (fStartDrag) {
if (dy >= mPosY && dy <= ypos + mPosY && dx >= mPosX && dx <= mPosX + xpos) {
fDrag = true;
fStartDrag = false;
}
} else if (fDrag) {
System.out.println("fDrag");
if (mPosX < 3)
mPosX = 3;
else if (mPosX > 400)
mPosX = 400;
else
mPosX = dx;
// mPosY = dy;
postInvalidate();
}
}
public void onScale(float scaleFactor) {
mScaleFactor *= scaleFactor;
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
postInvalidate();
}
}
}
My sample xml
<RelativeLayout
android1:id="#+id/r1"
android1:layout_width="620dip"
android1:layout_height="320dip"
android1:layout_alignParentBottom="true"
android1:layout_alignParentLeft="true"
android1:layout_marginBottom="94dp" >
</RelativeLayout>
<RelativeLayout
android1:id="#+id/r2"
android1:layout_width="620dip"
android1:layout_height="320dip"
android1:layout_alignParentBottom="true"
android1:layout_alignParentLeft="true"
android1:layout_marginBottom="94dp" >
</RelativeLayout>
If I use two relative layouts of different size and position then it works fine, onDraw() is not called multiple times
After trying so much I found this answer to my question.setDrawingCacheEnabled(true) prevents calling onDraw() multiple times.Its working for me.I hope I am going right.
View circleView;
r2 = (RelativeLayout) findViewById(R.id.r2);
circleView= r2;
circleView.setDrawingCacheEnabled(true);