I have problem with refreching canvas in my widget. I create custom widget and I want to repaint first cell on click.
There ins activity
public class TestView extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ElementView ev = (ElementView)findViewById(R.id.surface2);
ev.setOnClickListener(evOnClick);
}
public OnClickListener evOnClick = new OnClickListener() {
#Override
public void onClick(View v) {
ElementView temp=(ElementView)v;
temp.paint.setColor(Color.RED);
temp.canvas.drawRect(new Rect(0,0,90,90),temp.paint);
temp.postInvalidate();
}
};
}
and there is ElementView ( widget which I want to repaint )
public class ElementView extends View {
private final int width=100;
private final int height=100;
public Paint paint=null;
public Canvas canvas=null;
public ElementView(Context context) {
super(context);
paint = new Paint();
}
public ElementView(Context context, AttributeSet attr) {
super(context, attr);
paint = new Paint();
}
public ElementView(Context context, AttributeSet attr, int defaultStyles) {
super(context, attr, defaultStyles);
paint = new Paint();
}
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
int measuredWidth = MeasureSpec.getSize(widthSpec);
int measuredHeight = MeasureSpec.getSize(heightSpec);
setMeasuredDimension(this.width,this.height);
}
#Override
protected void onDraw(Canvas canvas) {
this.canvas=canvas;
// get the size of your control based on last call to onMeasure
int height = getMeasuredHeight();
int width = getMeasuredWidth();
// Now create a paint brush to draw your widget
paint.setColor(Color.GREEN);
//define border
this.canvas.drawLine(0, 0, 0, 99, paint);
this.canvas.drawLine(0, 0, 99,0, paint);
this.canvas.drawLine(99, 0, 99, 99, paint);
this.canvas.drawLine(0, 99, 99,99, paint);
//define cells
this.canvas.drawLine(0,50,99,50,paint);
this.canvas.drawLine(30,0,30,50,paint);
//draw green rectangle
this.canvas.drawRect(new Rect(0,0,50,50),paint);
//draw some text
paint.setTextSize(8);
paint.setColor(Color.RED);
String displayText = "test";
Float textWidth = paint.measureText(displayText);
int px = width / 2;
int py = height / 2;
this.canvas.drawText(displayText, px - textWidth / 2, py, paint);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
paint.setColor(Color.RED);
//change color of first cell
canvas.drawRect(new Rect(0,0,50,50),paint);
return super.dispatchTouchEvent(event);
}
}
When I click on ElementView it enter in Activity in onClick and pass through code without mistake or exception , but doesn't change view . Can anybody tell me where is mistake ?
I don't think writing to the Canvas outside of the onDraw method will have any effect on the screen.
Try setting a flag in your onClick method that you read in onDraw which will trigger your drawing routine.
Related
I'm creating a custom view in which I have a rectangle RectF object that have a specific height. I would like to increase the bottom Y point coordinate to a specific value with a progressive animation.
I've tried the following. I've created a method setBatteryState() that is called on a onclicked method in the activity that holds the custom view:
public class BatteryView extends View {
public int mCanvasWidth;
public int mCanvasHeight;
public RectF mBatteryHead;
public RectF mBatteryBody;
public RectF mBatteryBodyVolume;
public Canvas mCanvas;
public BatteryView(Context context) {
super(context);
}
public BatteryView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void init()
{
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
this.mCanvas = canvas;
float batteryHeadDistanceFromLeft = mCanvasWidth / 3;
float batteryHeadWidth = mCanvasWidth / 3;
float batteryBodyDistanceFromTop = mCanvasHeight / 5;
float batteryHeadHeight = mCanvasHeight / 5;
mBatteryHead = new RectF(batteryHeadDistanceFromLeft,0,2*batteryHeadWidth,batteryHeadHeight+5);
Paint batteryHeadPaint = new Paint();
batteryHeadPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
canvas.drawRect(mBatteryHead,batteryHeadPaint);
mBatteryBody = new RectF(0,(int)batteryBodyDistanceFromTop,mCanvasWidth,mCanvasHeight);
Paint batteryBodyPaint = new Paint();
batteryBodyPaint.setStyle(Paint.Style.STROKE);
batteryBodyPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
batteryBodyPaint.setStrokeWidth(10);
canvas.drawRect(mBatteryBody,batteryBodyPaint);
mBatteryBodyVolume = new RectF(12,(int)batteryBodyDistanceFromTop + 10,mCanvasWidth-12,mCanvasHeight/2);
Paint volumeBodyPaint = new Paint();
volumeBodyPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
canvas.drawRect(mBatteryBodyVolume,volumeBodyPaint);
}
public void setStateOnBattery(){
ObjectAnimator animateBottom = ObjectAnimator.ofFloat(mBatteryBodyVolume, "bottom", mBatteryBodyVolume.bottom, mCanvasHeight);
animateBottom.setDuration(1000).start();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
mCanvasWidth = MeasureSpec.getSize(widthMeasureSpec);
mCanvasHeight = MeasureSpec.getSize(heightMeasureSpec);
}}
ObjectAnimator should translate the rect mBatteryBodyVolume to the size of the canvas but nothing change...
Any Idea ?
Thanks in advance !
Use an asynchronous task with 2 major functions, draw and update. Every time update is called, increase the height variable by a constant. Then, in draw, draw your rectangle with height as a param. If you need code, just ask. :D
UPDATE
Create a 'runner' async task:
public class Runner extends Thread {
public volatile boolean running = true;
private Environment env;
public Runner(Environment E) {
env = E;
}
#Override
public void run() {
long lastTime = System.currentTimeMillis();
while(running) {
long now = System.currentTimeMillis();
long elapsed = now - lastTime;
env.update(elapsed);
env.draw();
lastTime = now;
}
}
public void shutdown() {
running = false;
}
}
In Environment, Do the following:
public void draw() {
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
canvas.drawRect(x-w, y-h, x+w, y+h, myPaint);
holder.unlockCanvasAndPost(canvas);
}
}
and the update method:
public void update(float elapsedTime) {
h+=myKonstant*elpasedTime;
}
Hope I Helped :D
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'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);
is it possible to change the color of the underline from a EditText dynamically?
Like the effect, when i focus it, then it turns into blue.
Hope you understand what i want to do.
Create your own customized EditText control.
Here's an example that I made just for you:
When selected, you just have to change mPaint.setColor(Color.GREEN); to another color
public class CustomEditText extends EditText {
private Rect mRect;
private Paint mPaint;
int widthMsSize;
int heightMsSize;
// we need this constructor for LayoutInflater
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.GREEN);
System.out.println("constructor");
}
protected void onMeasure(final int widthMeasureSpec,
final int heightMeasureSpec) {
// Extract the Ms (MesaureSpec) parameters
widthMsSize = MeasureSpec.getSize(widthMeasureSpec);
heightMsSize = MeasureSpec.getSize(heightMeasureSpec);
System.out.println("on measure");
// Satisfy contract by calling setMeasuredDimension
setMeasuredDimension(widthMsSize,
heightMsSize);
}
protected void onDraw(Canvas canvas) {
canvas.drawLine(5, heightMsSize - 10, widthMsSize - 5, heightMsSize - 10, mPaint); //draw underline
canvas.drawLine(8, heightMsSize - 10, 8, heightMsSize - 20, mPaint); //draw left corner
canvas.drawLine(widthMsSize - 8, heightMsSize - 10, widthMsSize - 8, heightMsSize - 20, mPaint); //draw right corner
super.onDraw(canvas);
}
}
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:orientation="vertical" >
<com.example.testanimationfadeinfadeout.CustomEditText
android:id="#+id/textedit"
android:layout_width="228dp"
android:layout_height="41dp"
android:ems="10"
android:hint="color is changed" />
</LinearLayout>
public static void setEditTextUnderlineColor(final EditText editText, final int focusedColor, final int unfocusedColor) {
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
editText.getBackground().setColorFilter(focusedColor, PorterDuff.Mode.SRC_ATOP);
return;
}
editText.getBackground().setColorFilter(unfocusedColor, PorterDuff.Mode.SRC_ATOP);
}
});
editText.getBackground().setColorFilter(unfocusedColor, PorterDuff.Mode.SRC_ATOP);
To customize edit Text I did the following way. It worked for me pretty simple way.
public class CardNumberText extends EditText {
boolean isFocus;
Paint mPaint;
Rect mRect;
int widthSize, heightSize;
public CardNumberText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initStyle();
}
private void initStyle() {
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#B3B3B3"));
}
public CardNumberText(Context context, AttributeSet attrs) {
super(context, attrs);
initStyle();
}
public CardNumberText(Context context) {
super(context);
initStyle();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect rect = mRect;
Paint paint = mPaint;
if (isFocus) {
mPaint.setStrokeWidth(3.0f);
mPaint.setColor(Color.parseColor("#80CBC4"));
} else {
mPaint.setStrokeWidth(1.5f);
mPaint.setColor(Color.parseColor("#B3B3B3"));
}
for (int i = 0; i < getLineCount(); i++) {
int baseline = getLineBounds(i, rect);
canvas.drawLine(rect.left, baseline + 20, rect.right, baseline,
paint);
}
}
#Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused) {
isFocus = true;
} else {
isFocus = false;
}
}
}
I have created custom widget which inherits View.
<com.test.www.view.ElementView
android:id="#+id/surface1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_weight="1"
/
>
and
public class ElementView extends View {
private final int width=100;
private final int height=100;
private Paint paint=null;
private Canvas canvas=null;
public ElementView(Context context) {
super(context);
paint = new Paint();
}
public ElementView(Context context, AttributeSet attr) {
super(context, attr);
paint = new Paint();
}
public ElementView(Context context, AttributeSet attr, int defaultStyles) {
super(context, attr, defaultStyles);
paint = new Paint();
}
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
int measuredWidth = MeasureSpec.getSize(widthSpec);
int measuredHeight = MeasureSpec.getSize(heightSpec);
setMeasuredDimension(this.width,this.height);
}
#Override
protected void onDraw(Canvas canvas) {
this.canvas=canvas;
// get the size of your control based on last call to onMeasure
int height = getMeasuredHeight();
int width = getMeasuredWidth();
// Now create a paint brush to draw your widget
paint.setColor(Color.GREEN);
//define border
this.canvas.drawLine(0, 0, 0, 99, paint);
this.canvas.drawLine(0, 0, 99,0, paint);
this.canvas.drawLine(99, 0, 99, 99, paint);
this.canvas.drawLine(0, 99, 99,99, paint);
//define cells
this.canvas.drawLine(0,50,99,50,paint);
this.canvas.drawLine(30,0,30,50,paint);
//draw green rectangle
this.canvas.drawRect(new Rect(0,0,50,50),paint);
//draw some text
paint.setTextSize(8);
paint.setColor(Color.RED);
String displayText = "test";
Float textWidth = paint.measureText(displayText);
int px = width / 2;
int py = height / 2;
this.canvas.drawText(displayText, px - textWidth / 2, py, paint);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
paint.setColor(Color.RED);
//change color of first cell
canvas.drawRect(new Rect(0,0,50,50),paint);
return super.dispatchTouchEvent(event);
}
}
Problem is that this ElementView doesn't react on click. I have override of dispatch event but it doesn't come in. Can anybody help me ? What to do to make this ElementView react on click ?
I would probably do something like this:
ElementView ev = (ElementView)findViewById(R.id.surface1);
ev.setOnClickListener(evOnClick);
public OnClickListener evOnClick = new OnClickListener() {
#Override
public void onClick(View v, int arg1) {
//DO ONCLICK STUFF
}
};
I'm not sure if you can actually put the onClick inside the view definition like you are trying to, but Id like if someone could show me how.