I'm developing a game which shows shapes randomly, I got the class but I got no idea how to call the custom view from my GameFragment. I was wondering if anyone can help me to achieve it.
CustomView.java
public class CustomViews extends View {
private final int SQUARE_SIZE = 500;
private RectF rectF;
private Paint paint;
private boolean CIRCLE = false;
private boolean RECTANGLE = false;
public CustomViews(Context context) {
super(context);
init(null);
}
public CustomViews(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomViews(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public CustomViews(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(#Nullable AttributeSet set) {
rectF = new RectF();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
randomColorGenerator();
}
public void rect(Canvas canvas) {
int height = canvas.getHeight() / 2;
int width = canvas.getWidth() / 2;
rectF.set(width - 200, height - 200, width + 200, height + 200);
}
private void circle(Canvas canvas) {
float cx;
float cy;
float radius = 200;
cx = canvas.getWidth() / 2;
cy = canvas.getWidth() / 2;
canvas.drawCircle(cx, cy, radius, paint);
}
private void randomColorGenerator() {
Random random = new Random();
int choices = 3;
switch (random.nextInt(choices)) {
case 0:
paint.setColor(Color.GREEN);
break;
case 1:
paint.setColor(Color.RED);
break;
case 2:
paint.setColor(Color.BLACK);
break;
}
}
public void randomShapeGenerator(Canvas canvas) {
Random random = new Random();
int numberOfMethods = 2;
switch (random.nextInt(numberOfMethods)) {
case 0:
rect(canvas);
rectSelected();
break;
case 1:
circle(canvas);
circleSelected();
break;
}
}
public boolean circleSelected(){
RECTANGLE = false;
return CIRCLE = true;
}
public boolean rectSelected( ) {
CIRCLE = false;
return RECTANGLE = true;
}
#Override
public void onDraw(Canvas canvas) {
randomShapeGenerator(canvas);
canvas.drawRect(rectF, paint);
}
}
Now I need to call it from my GameFragment where there are some buttons and whenever you click on them a new random shape must be generate. Is anyone got any idea?
Assuming your fragment has a LinearLayout with id main_content then this might help you to add your custom view programmatically:
Button btn = findViewById(R.id.yourbutton);
btn.setOnClickListener(v -> {
LinearLayout main_layout = findViewById(R.id.main_content);
CustomViews customView = new CustomViews(this);
customView.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
main_layout.addView(customView);
});
layoutParams might be also tuned according your needs.
You have to take into account where you want to display the customViews and also their sizes. For instance, if you have a linear layout with vertical orientation, you can add views in the following way:
main_layout.addView(customView, 400, 400);
This will display multiple customViews one below the other (until the screen's height is reached).
Indeed, CustomViews has to be modified a bit (here are two methods to be changed):
public void rect(Canvas canvas) {
int height = canvas.getHeight() / 2;
int width = canvas.getWidth() / 2;
rectF.set(width - 200, height - 200, width + 200, height + 200);
canvas.drawRect(rectF, paint);
}
#Override
public void onDraw(Canvas canvas) {
randomShapeGenerator(canvas);
}
Related
I am new to android & java. This might be very simple but I'm stuck... I am creating an app where by clicking on an "add" button a square is added in some default position on a canvas. You can then move and resize the square. Once you finish moving/ reshaping, my intention is for the square to maintain its new size/ position and for the user to be able to click on the add button where a new square will appear in the original default position. You can then again move / reshape the second square. And eventually add as many squares as you like (each unique).
I have managed to add the first square and i am also able to move/ reshape. I also created an array list to store the squares. My issue is that as soon as i add the second square, the shape and position of the first square are reset to the default position and size. And if i move the second square, so does the first one. So effectively its as if i end up with a stacked set of squares that all move and reshape at the same time.
The question is, how do i convert the variable information of position/ size to a constant before i add the square to the array?
I have the following in my main activity
addBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(addTimer != null) {
addTimer.cancel();
}
if (addclickcount>0) {
addBoxtoList();
}
addBox();
addclickcount++;
}
});
public void addBox(){ mCustomView.drawRoundRect();}
public void addBoxtoList(){mCustomView.addBoxtoList();}
Then in CustomView
public class CustomView extends View {
private ArrayList<Box> boxesArray = new ArrayList<>();
private RectF mRectSquare;
private Paint mPaintSquare;
private static RectF aRectSquare;
private static Paint aPaintSquare;
private int mSquareLeft, mSquareTop, dx, dy;
int colorSelected;
public CustomView(Context context) {
super(context);
init(null);
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init (#Nullable AttributeSet set){
mRectSquare = new RectF();
mPaintSquare = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintSquare.setColor(Color.BLUE);
mPaintSquare.setStyle(Paint.Style.FILL_AND_STROKE);
mPaintSquare.setAlpha(85);
}
public void drawRoundRect(){
if (General.addBoxCheck == true) {
dx = 200;
dy = 200;
mSquareLeft = scrwd - dx - 50;
mSquareTop = 50;
General.addBoxCheck = false;
}
mRectSquare.left = mSquareLeft;
mRectSquare.top = mSquareTop;
mRectSquare.right = mRectSquare.left + dx;
mRectSquare.bottom = mRectSquare.top + dy;
postInvalidate();
}
public void addBoxtoList(){
boxid = boxesArray.size() + 1;
aRectSquare = mRectSquare;
aPaintSquare = mPaintSquare;
Box boxa = new Box(boxid, aRectSquare, aPaintSquare);
boxesArray.add(boxa);
//postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawRoundRect(mRectSquare, 15, 15, mPaintSquare);
if(boxesArray != null) {
RectF rectF = new RectF();
Paint paint = new Paint();
for (int i = 0; i < boxesArray.size(); i++) {
paint = boxesArray.get(i).getPaint();
rectF = boxesArray.get(i).getRect();
canvas.drawRoundRect(rectF, 15, 15, paint);
}
}
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean value = super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
return true;
}
case MotionEvent.ACTION_MOVE:{
int x = (int) event.getX();
int y = (int) event.getY();
mSquareLeft = x;
mSquareTop = y;
postInvalidate();
return value;
}
}
return value;
}
Thanks
The question seems quite vague, if the values are changing then it means that you changed the values. If you don’t change the values of the array then essentially it should be a "constant". My assumption is that you are overwriting the array and therefore the value is changing. It would help to see your code.
Check the index of the stored square shapes and verify that you're not overwriting on any of the previous indexes.
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'm trying to draw text in a custom view that has an image as a background. Three things are being drawn.
The bitmap behind the circle
The red circle and
Text over it.
At the moment the circle and bitmap draw perfectly but the text doesn't display.
Code for custom view.
public class NotificationButtonView extends Button
{
private int mNumberOfNotifications = 0;
private Paint mNotificationPaint = new Paint();
private Paint mTextPaint = new Paint();
public NotificationButtonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public NotificationButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NotificationButtonView(Context context) {
super(context);
}
public void setNotificationNumber(int number)
{
this.mNumberOfNotifications = number;
this.invalidate();
}
public void addNotification()
{
this.mNumberOfNotifications++;
this.invalidate();
}
#Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
mNotificationPaint.setColor(Color.rgb(255,69,0));
mNotificationPaint.setAlpha(220);
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.BLACK);
mTextPaint.setStyle(Style.FILL);
mTextPaint.setTextSize(this.getWidth() / 3);
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setLinearText(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int diameter = this.getWidth() / 3;
/*
canvas.drawCircle(
this.getWidth() - diameter,
this.getHeight() - diameter,
diameter,
this.mNotificationPaint
);
*/
//Get the text bounds.
Rect foundBounds = new Rect();
mTextPaint.getTextBounds("1", 0, "1".length(), foundBounds);
foundBounds.offset(0, diameter);
canvas.drawText(
//String.valueOf(mNumberOfNotifications),
"1",
0,
foundBounds.bottom,
this.mTextPaint
);
//For testing the location of the text bounds.
canvas.drawRect(foundBounds, mNotificationPaint);
}
}
You are getting this.getWidth() 0 in onAttachedToWindow() as a result the text size is set to 0.
But you are getting this.getWidth() value in onDraw so in onDraw add this line
mTextPaint.setTextSize(this.getWidth() / 3);
First, I have a view like this:
When user press some text in edittext, I try to draw some rectangles in ImageView with specific coordinates. The problem is when the soft keyboard is hidden, all draw state of Imageview display wrong coordinates, look like it invalidated the imageview. I wonder is there any way to stop this.
Here is my Custom Drawable Imageview:
public class DrawableImageView extends ImageView {
private Paint ptBlur;
private List<Rect> rects;
private int originWidth;
private int originHeight;
public DrawableImageView(Context context) {
super(context);
initPaint();
}
public DrawableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public DrawableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPaint();
}
public void setRects(List<Rect> rects, int originWidth, int originHeight) {
this.originHeight = originHeight;
this.originWidth = originWidth;
this.rects = rects;
invalidate();
}
private void initPaint() {
ptBlur = new Paint();
ptBlur.setColor(Color.YELLOW);
ptBlur.setStyle(Paint.Style.STROKE);
ptBlur.setStrokeWidth(5);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (rects != null) {
int width = getWidth();
int height = getHeight();
for (Rect rect : rects) {
rect.bottom = Math.round(height * rect.bottom / originHeight);
rect.left = Math.round(width * rect.left / originWidth);
rect.right = Math.round(width * rect.right / originWidth);
rect.top = Math.round(height * rect.top / originHeight);
canvas.drawRect(rect, ptBlur);
}
}
}
}
The OnEditorActionListener for EditText:
// get the rectangles and draw
List<Rect> rects = DataUtil.getHighlightRectList(searchText);
drawableImageView.setRects(rects, pageWidth, pageHeight);