I have extended an ImageView and calling a method of ImageView from outside class , inside a Thread. Inside the method I have tried using invalidate postValidate and everything but it never called the onDraw method , is it something to do with the calling method-
public class TestImageView extends ImageView {
public FacePreviewImageView(Context context) {
super(context);
}
public void process(String strImageFilePath) {
//doing some operation
invalidate();
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
Log.i("CAME INSIDE ", ""+faces.total());
if(faces.total()>0){
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(20);
String s = "Processed Face";
float textWidth = paint.measureText(s);
canvas.drawText(s, (getWidth() - textWidth) / 2, 20, paint);
CvRect r = new CvRect(cvGetSeqElem(faces, 0));
int x = r.x(), y = r.y(), w = r.width(), h = r.height();
canvas.drawRect(new Rect(x, y, x+w, y+h), paint);
}
super.onDraw(canvas);
}
}
and my calling method looks like -
new Handler().post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
((TestImageView )imageView).process(pictureFile.getAbsolutePath());
}
});
One more point to add-
I tried to add this view directly inside layout file-
<FrameLayout
android:id="#+id/ll2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:orientation="horizontal" >
<com.example.defaultfacetracker.TestImageView
android:id="#+id/imageView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</FrameLayout>
but its throwing exception while launching. SO I finally changed the code to-
<ImageView
android:id="#+id/imageView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#android:drawable/toast_frame" />
And in a class I am just creating a new instance of TestImageView to work for.
If that is the reason to do something here.
Have you tried to set:
setWillNotDraw(false);
In your constructor?
#Override
public FacePreviewImageView(Context context) {
this(context, null, 0);
}
#Override
public FacePreviewImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
public FacePreviewImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(false);
}
setWillNotDraw(false); doesn't work for me.
I had the same issue, here, and I figured out checking that my child class of ImageView has visibility VISIBILE and not GONE.
If it has GONE value for visibility, onDraw will not be called.
Related
If a custom view has several shapes, Is it possible to animate only one of them?
For eg: for one of my application, 2 circles, one inner and another outer are drawn on a custom view. While I tried to animate using scale animation, I see that both the circles gets animated where as I need only one of them to.
One of the solution that occurred to me is to have multiple custom views.
But not sure if it is the right way to do it.
Are there alternate better solution to it?
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.test.customanimation.CustomView
android:id="#+id/circular_progress"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="#+id/scale_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scale Up"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<Button
android:id="#+id/scale_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="Scale Down"/>
</android.support.constraint.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private CustomView mCustomView;
private Button mScaleUpBtn;
private Button mScaleDownBtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCustomView = findViewById(R.id.circular_progress);
mScaleUpBtn = findViewById(R.id.scale_up);
mScaleUpBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCustomView.scaleUpAnimation(5000);
}
});
mScaleDownBtn = findViewById(R.id.scale_down);
mScaleDownBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCustomView.scaleDownAnimation(5000);
}
});
}
}
CustomView.java
public class CustomView extends View {
private Paint OuterCirclePaint,InnerCirclePaint;
float mCircleX,mCircleY,mInnerCircleRadius,mOuterCircleRadius;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int
defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int
defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init(){
OuterCirclePaint = new Paint();
OuterCirclePaint.setColor(Color.GREEN);
OuterCirclePaint.setStrokeWidth(20);
OuterCirclePaint.setStyle(Paint.Style.STROKE);
InnerCirclePaint = new Paint();
InnerCirclePaint.setColor(Color.BLACK);
InnerCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
mCircleX = getWidth()/2;
mCircleY = getHeight()/2;
if(mCircleX < mCircleY) {
mInnerCircleRadius = (getWidth() / 2) - 100;
mOuterCircleRadius = (getWidth() / 2) - 40;
}
else {
mInnerCircleRadius = (getHeight() / 2) - 100;
mOuterCircleRadius= (getHeight() / 2) - 40;
}
canvas.drawCircle(mCircleX,mCircleY,mOuterCircleRadius,OuterCirclePaint);
canvas.drawCircle(mCircleX,mCircleY,mInnerCircleRadius,InnerCirclePaint);
}
public void scaleDownAnimation(int duration){
ScaleAnimation fade_in = new ScaleAnimation(1.0f,0.5f,1.0f,0.5f,
Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
fade_in.setDuration(duration);
fade_in.setFillAfter(true);
this.startAnimation(fade_in);
}
public void scaleUpAnimation(int duration){
ScaleAnimation fade_out = new ScaleAnimation(0.5f,1.0f,0.5f,1.0f,
Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
fade_out.setDuration(duration);
fade_out.setFillAfter(true);
this.startAnimation(fade_out);
}
}
You are already overriding onDraw() and providing your own methods to handle the animations. IMO this approach is best for performance, so I'd keep it that way and only switch over to another animation framework, namely Property Animations
In order to redraw only the inner circle during an animation, I'd suggest using ValueAnimator and ValueAnimator.AnimatorUpdateListener for the animations.
Let's introduce some new fields for CustomView
private float scaleFactor = 1f;
private ValueAnimator scaleUpAnimator;
private ValueAnimator scaleDownAnimator;
private ValueAnimator.AnimatorUpdateListener updateListener;
Initialize them as follows
private void initAnimations() {
scaleUpAnimator = ValueAnimator.ofFloat(0.5f, 1.0f);
scaleDownAnimator = ValueAnimator.ofFloat(1.0f, 0.5f);
updateListener = new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
scaleFactor = (float)animation.getAnimatedValue();
CustomView.this.invalidate();
}
};
scaleUpAnimator.addUpdateListener(updateListener);
scaleDownAnimator.addUpdateListener(updateListener);
}
Change the line for the inner circle in onDraw()
canvas.drawCircle(mCircleX, mCircleY, mInnerCircleRadius * scaleFactor, innerCirclePaint);
... and start the animations like this
public void scaleDownAnimation(int duration){
scaleDownAnimator.setDuration(duration);
scaleDownAnimator.start();
}
public void scaleUpAnimation(int duration){
scaleUpAnimator.setDuration(duration);
scaleUpAnimator.start();
}
So basically I have some image views drawn over my canvas, but when I try to set up onclick listeners and try to handle events, it doesn't work.
public class DrawView extends View implements OnClickListener{
Paint paint = new Paint();
RectF rf = new RectF(30, 30, 80, 80);
BallView ball = new BallView(getContext());
Bitmap bmp = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.ic_launcher);
public DrawView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
ball.setOnClickListener(this);
ball.draw(canvas);
canvas.drawBitmap(bmp, 110, 10, paint);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(getContext(), "Mock", Toast.LENGTH_SHORT).show();
}
}
BallView.java
public class BallView extends ImageView {
Paint b = new Paint();
public BallView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public void onDraw(Canvas c){
b.setColor(Color.BLUE);
c.drawCircle(50, 50, 40, b);
}
}
Basically the answer is: Your view is not receiving touch events, because it's not in views hierarchy, so click callback is also not called. Actually, view should be in View hierarchy (in other words, added to some upper level view and finally, to the window) in order to participate is user interactions.
So, if You want to implement somthing covering Your view, then it should be ViewGroup or shouldn't be related to view at all and be some abstract container.
E.g. using the way below click works without any issues (but You will need second view or modify BallView to draw bmp):
DrawView:
public class DrawView extends FrameLayout implements View.OnClickListener {
BallView ball = new BallView(getContext());
public DrawView(Context context) {
this(context, null, 0);
}
public DrawView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public DrawView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
addView(ball);
ball.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "Mock", Toast.LENGTH_SHORT).show();
}
}
BallView class stays the same.
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.ruinalst.performance.tests.views.DrawView
android:id="#+id/oscillator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</RelativeLayout>
I have a class that extends a view.
This class is called in xml to build my view. Now the view call the onDraw automatically the onDraw function on loaded. But how i can do what is do on onDraw() function only after i click in that view?? In conclusion i need to execute the code inside onDraw() only after the click in view.
DrawView.java:
public class DrawView extends View {
Paint mPaint = new Paint();
Context context;
public DrawView(Context context, AttributeSet attrs){
super(context, attrs);
this.context = context;
// setWillNotDraw(true);
}
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
mPaint.setColor(Color.MAGENTA);
canvas.drawRect((float) (getWidth()*0.3), (float) (getHeight()*0.3), getWidth(), getHeight(), mPaint);
main.xml:
<LinearLayout
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1">
<com.example.sliding.DrawView
android:id="#+id/tv_listRow_item1"
android:tag="tv_listRow_item1_1"
android:layout_height="0dip"
android:layout_width="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:width="100dip"
android:height="30dip"
android:background="#drawable/textview_listrow_border"/>
</LinearLayout>
main.java:
((DrawView)v.findViewById(R.id.tv_listRow_item1)).setOnClickListener(listener);
private View.OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
}
}
Any suggestions? Thanks for your time and attention.
Try overriding the
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// your actions here
}
}
then call invalidate() function, which will set a flag to call the onDraw() of your view.
I'm working on a Livewallpaper with a Thread Painting all Objects and Custom Objects extended from View. The Problem is that my custom view doesn't fire on click....
Here are the Code Snippeds:
in my WallpaperService Class the OnTouch ist given to my Painting Thread:
#Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
painting.doTouchEvent(event);
}
Then in the constructor of my PaintingThread I'm creating instances of my custom view:
public LiveWallpaperPainting(SurfaceHolder surfaceHolder, Context context) {
for(int i = 0; i < numberOfObj;i++ ){
obj.add(new Obj(context,objBitmap, objBitmap2, mCanvasWidth, mCanvasHeight, 32, 32 , 20, 10));
}
Then in the Constructor of my Obj:
super(context);
this.context = context;
this.setEnabled(true);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
this.setClickable(true);
this.setOnClickListener(this);
The Class implements OnClickListener.
But when I'm logging the onClick nothing happens....:
#Override
public void onClick(View v) {
Log.d(TAG, "clicked");
}
I'm getting crazy because I tried so much, but nothing worked... :( Please help me.
I think the OnClick is catched before my Obj can react?? But don't know why....
I hope I gave you all details needed...
Yumi
The following code works. Have you on the Activity a other OnClickListener?
public class CustomView extends View implements OnClickListener {
Paint paint;
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawRect(0.f, 0.f, 240.f, 240.f, paint);
this.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.d("CustomView", "Click");
}}
main.xml
<com.examples.view.CustomView
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
I facing problem to draw rectangle at subclass of my android custom view class. Each time super class onDraw method works.But subclass onDraw method never executed. Super class will draw a rectangle and subclass will draw 4 rectangle within the super-class drawn rectangle.I can't fixed this problem.please help me.
Here is my sample code.
SuperClass:
public class ColorFanView extends View{
public ShapeDrawable[] mDrawables;
public ColorFanView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ColorFanView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ColorFanView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvasObject) {
super.onDraw(canvasObject);
int x = 100;
int y = 100;
int width = 80;
int height = 200;
Paint thePaint = new Paint();
thePaint.setColor(Color.WHITE);
RectF rectnagle1 = new RectF(x,y,x+width,y+height);
canvasObject.drawRoundRect(rectnagle1, 10.0f, 10.0f, thePaint);
}
}
Subclass:
public class ColorFanStack extends ColorFanView{
public ColorFanStack(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public ColorFanStack(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ColorFanStack(Context context) {
super(context);
initView();
}
public void initView() {
mDrawables = new ShapeDrawable[4];
float[] outerR1 = new float[] { 12, 12, 12, 12, 0, 0, 0, 0 };
mDrawables[0] = new ShapeDrawable(new RoundRectShape(outerR1, null, null));
mDrawables[0].getPaint().setColor(Color.RED);
mDrawables[1] = new ShapeDrawable(new RectShape());
mDrawables[1].getPaint().setColor(Color.WHITE);
mDrawables[2] = new ShapeDrawable(new RectShape());
mDrawables[2].getPaint().setColor(Color.BLUE);
mDrawables[3] = new ShapeDrawable(new RectShape());
mDrawables[3].getPaint().setColor(Color.YELLOW);
}
#Override
protected void onDraw(Canvas canvasObj) {
super.onDraw(canvasObj);
int x = 100;
int y = 100;
int width = 80;
int height = 40;
int canvasSpace =5;
for (Drawable dr : mDrawables) {
dr.setBounds(x, y, x + width, y + height);
dr.draw(canvasObj);
y += height + canvasSpace;
}
}
}
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myViewGroup" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.test.colorfan.ColorFanView
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/firstView" />
</RelativeLayout>
Please help me regarding this issue. Hopefully, I will get a reply soon.
My guess is that your layout (please edit the question to include your layout), is defining your ColorFanView instances in such a way that they have 0 height or width; therefore, the parent View does not draw them.
EDIT 7/27/2011: Habibur Rahman added his layout XML to the question. This is the new answer:
Your two classes work, but you added the wrong one to your layout (you should have used ColorFanStack instead of ColorFanView). An instance of ColorFanStack will inherit the drawing of ColorFanView (by virtue of the fact that your ColorFanStack.onDraw() method calls super.onDraw()). I think that that was the behavior that you were trying to achieve.
Here is the XML that I used with the classes as you defined them:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.habecker.demo.ColorFanStack
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/secondView" />
</RelativeLayout>