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.
Related
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.
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 am having trouble putting all the pieces together. I have 3 classes: DefaultActivity that extends Activity, MyView that extends SurfaceView, and ResistorView that extends a View.
What I want to know is how draw a resistor every time I click the button bAddResistor from the DefaultActivity class? Here are some parts from the 3 classes:
DefaultActivity.java
public class DefaultActivity extends Activity {
//MyView myView;
TextView myText;
ResistorView myResistor;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//myView = (MyView) findViewById(R.id.my_view);
//myView = new MyView(this, null);
//myResistor = new ResistorView(this);
//myView.setBackgroundColor(Color.WHITE);
//myView.setScrollContainer(true);
setContentView(R.layout.main);
final Button bAddResistor = (Button) findViewById(R.id.bAdd);
bAddResistor.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
Global.numberOfResistors++;
Log.d("ButtonADD", "Button Add has been clicked");
}
});
}
}
MyView.java
public class MyView extends SurfaceView implements SurfaceHolder.Callback {
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas();
onDraw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
ResistorView.java
public class ResistorView extends View {
private Path mSymbol;
private Paint mPaint;
//...Override Constructors...
public ResistorView(Context context) {
super(context);
init();
}
public ResistorView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mSymbol = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(2);
mPaint.setColor(-7829368);
mPaint.setStyle(Paint.Style.STROKE);
//...Your code here to set up the path,
//...allocate objects here, never in the drawing code.
mSymbol.moveTo(0.0F, 0.0F);
mSymbol.lineTo(0.0F, 50.0F);
mSymbol.lineTo(16.666666F, 58.333332F);
mSymbol.lineTo(-16.666666F, 75.0F);
mSymbol.lineTo(16.666666F, 91.666664F);
mSymbol.lineTo(-16.666666F, 108.33333F);
mSymbol.lineTo(16.666666F, 124.99999F);
mSymbol.lineTo(-16.666666F, 141.66666F);
mSymbol.lineTo(0.0F, 150.0F);
mSymbol.lineTo(0.0F, 200.0F);
mSymbol.offset(100.0F, 20.0F);
}
//...Override onMeasure()...
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Use this method to tell Android how big your view is
setMeasuredDimension(50, 50);
}
//...Override onDraw()...
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mSymbol, mPaint);
}
}
and the main.xml layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/RelativeLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:ignore="HardcodedText" >
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RightLayout"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:background="#00000000"
android:orientation="vertical" >
<Button
android:id="#+id/bAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add RES" />
<Button
android:id="#+id/bDeleate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Del RES" />
</LinearLayout>
<com.defaul.android.MyView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_toLeftOf="#id/RightLayout"
android:background="#ff000000" />
</RelativeLayout>
If I put the drawing code in MyView.onDraw it works fine, the resistor is drawn. What I want is to click a button and draw the resistor on the surface, so every time I click I want a resistor to be added to the surface view.
Probably I'm approaching this problem in the opposite direction. If anyone can point me in the right direction I would really appreciated it.
Since I could not find a way to do what I wanted with SurfaceView I just created a class that extends View and then just add that view to my main layout.
In case it might help someone, below is a simplified version of my code:
public class CircuitSolverActivity extends Activity {
/** Called when the activity is first created. */
RelativeLayout mLayout;
MyView mView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLayout = (RelativeLayout)findViewById(R.id.elementContainer);
}
public void bAddView(View view){
mView = new MyView(context);
mView.setBackgroundColor(Color.TRANSPARENT);
mLayout.addView(mView);
}
MyView class:
public class ResistorView extends View {
public ResistorView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ResistorView(Context context){
super(context);
init();
}
private void init() {
mSymbol = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(2);
mPaint.setColor(-7829368);
mPaint.setStyle(Paint.Style.STROKE);
mSymbol.moveTo(0.0F, 0.0F);
mSymbol.lineTo(0.0F, 50.0F);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mSymbol, mPaint);
}
I have created one view, view code is,
public class MyDraw extends View{
//List<Point> mArryLstpoints = new ArrayList<Point>();
Paint paint =new Paint();
Paint mPaintAlphabet = new Paint();
public MyDraw(Context context) {
super(context);
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
// TODO Auto-generated constructor stub
}
public MyDraw(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
mPaintAlphabet.setDither(true);
mPaintAlphabet.setColor(0xFFFF0000);
mPaintAlphabet.setStyle(Paint.Style.STROKE);
mPaintAlphabet.setStrokeJoin(Paint.Join.ROUND);
mPaintAlphabet.setStrokeCap(Paint.Cap.ROUND);
mPaintAlphabet.setStrokeWidth(3);
mPaintAlphabet.setTextSize(400);
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.GRAY);
canvas.drawText("A",100,350, mPaintAlphabet);
System.out.println("in on draw");
for(Point mPoint:MyAlphabetsActivity.mArryLstpoints)
canvas.drawCircle(mPoint.x, mPoint.y, 12, paint);
}
}
I want use this view in layout and I want to set background images for the view. I am using following code,
<?xml version="1.0" encoding="utf-8"?>
<com.qteq.myalphabets.MyDraw
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" ></com.qteq.myalphabets.MyDraw>
My activity class is,
public class MyAlphabetsActivity extends Activity {
/** Called when the activity is first created. */
MyDraw mMyDraw;
Button mBtnOk;
AttributeSet attributeSet;
public static List<Point> mArryLstpoints = new ArrayList<Point>();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/*
* getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
* WindowManager.LayoutParams.FLAG_FULLSCREEN);
*/
/*
* mMyDraw=new MyDraw(this); setContentView(mMyDraw);
* mMyDraw.requestFocus();
*/
mMyDraw = (MyDraw) findViewById(R.id.mMyDraw_layout);
mMyDraw.bringToFront();
mMyDraw.requestFocus();
mMyDraw.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
Point mPoint=new Point();
mPoint.x=event.getX();
mPoint.y=event.getY();
System.out.println("in on touch");
mArryLstpoints.add(mPoint);
System.out.println("Array list is-----"+mArryLstpoints);
setContentView(R.layout.main);
return true;
}
});
// setContentView(R.layout.main);
}
}
class Point{
public float srtx;
float x, y;
#Override
public String toString() {
System.out.println("in on point");
return x + ", " + y;
}
}
and Displaying and onTouch Method called only one time.
You are Adding view with fill_parent width and height , so no space to add other views like buttons and images .
one of the possible implementation
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.qteq.myalphabets.MyDraw
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_contentt" ></com.qteq.myalphabets.MyDraw>
<Button/>
<ImageView/>
</LinearLayout>
Dont forget to add constructor
public MyDraw(Context context, AttributeSet attributeSet) {
super(context, ttributeSet);
in MyDraw.java
******** *editing* ******
in onTouch()
return super(v, event) instead of true ;
also check if you need to write setOnTouchListener() to view or canvas .
You can place your view within a layout in your XML and there can be other elements, as well, within that layout. You can set layout background to something, too.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#drawable/mybackground">
<com.qteq.myalphabets.MyDraw
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
I use the following View to draw a bitmap and move it around.
public class DrawView extends View {
private ColorBall ball;
public DrawView(Context context) {
super(context);
setFocusable(true);
ball = new ColorBall(context,R.drawable.bol_groen, points);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(), null);
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// do stuff...
}
}
In the starting Activity, the layout is set using setContentView(new DrawView(this));
I want add a button to the screen and when I click on the button, I want a new bitmap to be added. How do I add a button to this screen?
EDIT: This is my main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<Button android:text="Button"
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<com.example.DrawView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Set activity's layout from xml. Put there button and your custom view (you can make it GONE if you don't want it to be visible).
But before making it you should have one more constructor for your view
public DrawView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setFocusable(true);
ball = new ColorBall(context,R.drawable.bol_groen, points);
}