I create a custom view and add selector to my custom view. Selector works, but background stretches full width and height. And background must displays what I draw.
Full code http://pastebin.com/dmF6DiP8
#Override
protected void onDraw(Canvas canvas)
{
Log.d(TAG, "init onDraw");
if (canvas != null && mDrawable != null)
{
mDrawable.setState(getDrawableState());
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBackgroundPaint);
mDrawable.draw(canvas);
}
}
Now normal state:
Now pressed state:
Must be:
What you are doing is drawing the circle first and then drawing a your drawable on top of it. The circle is underneath the drawable so you can't see it. The circle is also being drawn with the same color every time and it is not taking it from the drawable at all.
What you need to do is apply the color to the circle depending on the selector state and remove the call to draw the drawable on the canvas.
Related
I am trying to animate a custom view
I implemented a circular progress bar custom view, exactly similar to this one
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (totalCount == 0 || progressCount == 0)
{
// No progress, set a different background color and draw a arc and set a icon at the center
progressBackgroundPaint.setColor(outerNoProgressBackgroundColor);
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, PROGRESS_END_DEGREE, false, progressBackgroundPaint);
drawInnerBackgroundWithState(canvas, ProgressState.NO_PROGRESS);
drawCenterIconWithState(centerIconEmptyBitmap, canvas, ProgressState.NO_PROGRESS);
}
else
{
// Change the color first for a progress bar
progressBackgroundPaint.setColor(outerProgressBackgroundColor);
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, PROGRESS_END_DEGREE, false, progressBackgroundPaint);
// Then draw an arc on top of it marking progress
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, getAngle(), false, progressPaint);
// set inner background color and tint center icon with state-appropriate color and draw it in the center
// of the progress circle
if (progressCount < totalCount)
{
canvas.drawOval(viewRect, innerBackgroundPaint);
drawCenterIconWithState(centerIconProgressBitmap, canvas, ProgressState.IN_PROGRESS); // draw bit map function
}
else
{
canvas.drawOval(viewRect, completeBackgroundPaint);
drawCenterIconWithState(centerIconProgressBitmap, canvas, ProgressState.COMPLETE); // draw bitmap function
}
}
}
I call this onDraw using ValueAnimator
public void drawProgressAnimation(float progress)
{
ValueAnimator animator;
if (animator != null)
animator.cancel();
animator = ValueAnimator.ofFloat(0f, progress);
animator.setDuration(600);
progressAnimator.addUpdateListener(animation ->
{
float progress1 = (float)animation.getAnimatedValue();
setProgress(progress1, true);
});
progressAnimator.start();
}
And invalidating in setProgress method which calls onDraw.
But, I need to achieve something like this,
I tried using Animator set along with ValueAnimator, but it didn't work. I tried to call onDraw with sequentially by setting a variable inside the lambda but in vain.
I am not sure whether can do this using AnimatedVectorDrawable. Can somebody help?
I used this repo as reference and implemented what i want. Instead of using random counter to set the speed. I used value animation and used the progress value for every frame to set the arc rate. And used the counter to draw circles that would grow from Zero to radius.
if (isComplete) {
canvas.drawArc(mRect, START_DEGREE, getSweepAngle(), false, progressPaint);
// animate circle progress when arc reaches partial sweep angle
if (getSweepAngle() >= partialSweepAngle)
{
innerBackground.setColor(innerCompleteColor);
circleCounter += circleProgressRate;
if (circleCounter <= radius)
canvas.drawCircle(mRect.centerX(), mRect.centerY(), circleCounter,
innerBackground);
else
canvas.drawCircle(mRect.centerX(), mRect.centerY(), radius, innerBackground);
}
if (circleCounter >= radius)
// draw the icon
}
I am writing a custom renderer for Xamarin.Forms. I am overriding the class ImageRenderer to do some tweaks to the image.
However, I want to overlay some parts of the image with circles, so I override the method void OnDraw(Canvas canvas).
Then I modify the Canvas a bit by drawing some circles:
protected override void OnDraw(Canvas canvas)
{
var paint = new Paint
{
Color = Color.Red
};
paint.SetStyle(Paint.Style.Fill);
foreach (var mapObject in _control.PointSource)
{
canvas.DrawCircle(mapObject.Location.X, mapObject.Location.Y, 100 / _scaleFactor, paint);
}
base.OnDraw(canvas);
}
However, everything is drawn on the background. The actual image is always on top. How can one draw circles on top of the image?
https://developer.xamarin.com/api/member/Android.Views.View.OnDraw/
In the docs they say the following:
the canvas on which the background will be drawn
So the current behaviour was expected.
I managed to fix it by moving the circle logic to the function: void Draw(Canvas canvas).
I am working on slide menu view which extends SurfaceView but when I try to change size of the view a canvas does not change.
For debug purpose, I am changing size of the view calling method:
public void changeSize() {
width += 10;
getLayoutParams().width = width;
this.setLayoutParams(getLayoutParams());
}
Then in a draw function I paint the canvas white and draw red line diagonally across the view:
public void draw() throws Exception {
SurfaceHolder surfaceHolder = getHolder();
if (surfaceHolder.getSurface().isValid()) {
Canvas canvas = surfaceHolder.lockCanvas();
Log.i(TAG, "draw > canvas size: " + canvas.getWidth() + " " + canvas.getHeight() );
paint.setColor(Color.rgb(255,0,0));
paint.setStrokeWidth(5);
canvas.drawColor(Color.rgb(255, 255, 255));
canvas.drawLine(0, getLayoutParams().height, getLayoutParams().width, 0, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
What I see is a white rectangle with fixed size and part of red line:
demo.
Also, when I call this.getWidth() I get unchanged width.
Why view size is not changing? Is it the same reason that the canvas is not changing? What can I do to fix it?
I found reason for myself.
I did not mention about something - that I thought was not important - I called changeSize() via other thread than created the view and this is not allowed because: Only the original thread that created a view hierarchy can touch its views. And this problem was many time solved, e.g. here
I could use Activity.runOnUiThread() method but I did not want to pass the Activity to the view so I use View.post() method in the changeSize() like this:
Runnable changeParams = new Runnable() {
#Override
public void run() {
getLayoutParams().width = width;
setLayoutParams(getLayoutParams());
}
};
this.post(changeParams);
Update:
Maybe I do not know about proper way of using the UI Thread but whatever I tried I can not change view parameters smoothly.
Some workarounds to implement a slide menu:
Do [Android navigation Drawer][4] -- you can custiomize it with restictions e.g. you can not do the navigation drawer moving from top.
Use [Android animations][5] -- with it you can move or scale a view but can not resizing it.
I can not put more than 2 links with my reputation:
(4): developer.android.com/training/implementing-navigation/nav-drawer.html
(5): developer.android.com/training/animation/index.html
By doing getLayoutParams().width = width; you are changing layout params of the View. But you are performing drawing on a Canvas that owns SurfaceHolder, and not the one an ordinary View will provide you.
So, in order to change the size of the SurfaceView, you have to interact with SurfaceHolder:
surfaceView.getHolder().setFixedSize(width, height);
I have created a custom view class to use in a project I'm working on. To put is simply, I'm displaying an image, then adding images on top of the original image (currently by clicking on the image, but that's not final).
I have these 2 methods in my custom view:
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
for (Drawable d : drawableList)
{
d.draw(canvas);
}
}
public void AddPoint(float x, float y)
{
Drawable tempDrawable = pin;
tempDrawable.setBounds((int)x, (int)y, (int)x + 50, (int)y + 50);
drawableList.add(tempDrawable);
invalidate();
}
The AddPoint() method is called in an OnTouchListener and is passes the coordinates of the touch event.
The way it currently works is it displays the main image, but will only display the most recent images where I clicked, previous ones just disappear.
Does anybody know what I am doing wrong here?
I've figured out what I was doing wrong.
The line of code:
Drawable tempDrawable = pin;
I changed to:
Drawable tempDrawable = mainRes.getDrawable(R.drawable.pin);
I got mainRes from the init(Context context) method I wrote in the custom View class.
I've customized imageview but its just rotating its bitmap not the whole view. I don't want to use animation because I'm dragging imageview as well so moving animated imageivew results weird, so sticking to onDraw/draw, plus overriding draw is nothing doing special.
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
canvas.save();
canvas.rotate(rotationAngle, rotationW, rotationH);
super.onDraw(canvas);
canvas.restore();
}
how can I rotate the whole view not only its bitmap??
I did not get the provided answers intend here, but using
android:clipChildren="false"
for the parent ViewGroup
and overriding draw instead of onDraw did the job for me
For further information see:
Rotating imageview and its background without cropp
One must check width and height of the imageview, if Height > width then at the time of rotation width must be equal to height and same with height.
Make width and height equal to fill_parent and then rotate/translate/scale via matrix.
Starting with API 11, there is the following method available for each view:
.setRotation(float angle);
http://developer.android.com/reference/android/view/View.html#attr_android:rotation
Before API 11 you could add a rotate to the main-element of your view:
#Override
protected void onDraw(Canvas c) {
c.save();
c.rotate(...);
super.onDraw(c);
c.restore();
}