Question

I have created a custom view named CustomViewVisual1 that draws on screen objects of the class Particle. I use this custom view in a class named LayoutCanvas that extends Activity. Neither onTouch() or onProgressChanged() events in LayoutCanvas seem to cause my view to redraw even though I'm calling invalidate and have also tried postInvalidate(). The values of my objects change but I get no redraw! To be honest I'm new to Android and not aware of any good practises on how to design my classes and where to implement what, so if anyone has a better solution to what I'm trying to achieve, please let me know. Thanks in advance!

This is the Particle class:

    public class Particle {
    Rect rect = new Rect();
    float similarity;
    Point p = new Point();
    float x;
    float y;

    float tx = 240;
    float ty = 320;

    Matrix transform = new Matrix();

    float r;
    Paint _paint = new Paint();
    Paint tempp = new Paint();

    Particle(){}

    Particle(int width, float similarity){
        this.similarity = similarity;
        x = MyMath.map(similarity, 0, 1, 0, width/2);
        //y = MyMath.myRandom(-20, 20);
        y = 0;
        //p.set((int) MyMath.map(similarity, 0, 1, 0, width/2), (int)MyMath.myRandom(0, 360));
        p.set((int)x, (int)y);
        r = MyMath.myRandom(0, 360);
        //recalculateCoordinates();
        _paint.setColor(Color.WHITE);
        tempp.setColor(Color.GREEN);
        //rotate(r);

    }


    public void display(Canvas canvas, Paint paint, float scale){
        //canvas.drawLine(canvas.getWidth()/2, 0, canvas.getWidth()/2, canvas.getHeight(), _paint);
        //canvas.drawLine(0, canvas.getHeight()/2, canvas.getWidth(), canvas.getHeight()/2, _paint);
        canvas.save();
        canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
        //canvas.translate(tx, ty);
        canvas.rotate(r);
        //canvas.scale(scale, scale);
        paint.setColor(colorize());
        canvas.drawRect(-p.x, p.y, -p.x+10, p.y+10, paint);
        canvas.restore();

        canvas.save();
        canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
        canvas.drawLine(0, 0, 200, 0, tempp);
        canvas.drawText("0'", 100, 8, tempp);
        canvas.restore();

        canvas.save();
        canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
        canvas.rotate(45);
        canvas.drawLine(0, 0, 200, 0, tempp);
        canvas.drawText("45''", 100, 8, tempp);
        canvas.restore();

        canvas.save();
        canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
        canvas.rotate(90);
        canvas.drawLine(0, 0, 200, 0, tempp);
        canvas.drawText("90'", 100, 8, tempp);
        canvas.restore();

        canvas.save();
        canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
        canvas.rotate(180);
        canvas.drawLine(0, 0, 200, 0, tempp);
        canvas.drawText("180'", 100, 8, tempp);
        canvas.restore();
    }

    public int colorize(){
        return Color.argb(100, Math.round(255-MyMath.map(similarity, 0, 1, 0, 255)), 0, Math.round(MyMath.map(similarity, 0, 1, 0, 255)));
    }

    public void rotate(float degrees){
        transform.setTranslate(120, 160);
        transform.setRotate(degrees, 0, 0);

        // Create new float[] to hold the rotated coordinates
        float[] pts = new float[2];

        // Initialize the array with our Coordinate
        pts[0] = p.x;
        pts[1] = p.y;

        // Use the Matrix to map the points
        transform.mapPoints(pts);

        p.set((int)pts[0], (int)pts[1]);
        // NOTE: pts will be changed by transform.mapPoints call
        // after the call, pts will hold the new cooridnates

        // Now, create a new Point from our new coordinates
        //Point newPoint = new Point((int)pts[0], (int)pts[1]);

        // Return the new point
        //p.set(newPoint.x, newPoint.y);
        //return newPoint;
    }

    public void translate(int x, int y){
        tx = x;
        ty = y;
    }

    public void recalculateCoordinates(){
        x = (float) ( (x * android.util.FloatMath.cos(r) ) - (y * android.util.FloatMath.sin(r) ) );
        y = (float) ( (x * android.util.FloatMath.sin(r) ) - (y * android.util.FloatMath.cos(r) ) );
    }
    /**
     * @return the similarity
     */
    public float getSimilarity() {
        return similarity;
    }

    /**
     * @param similarity the similarity to set
     */
    public void setSimilarity(float similarity) {
        this.similarity = similarity;
    }

    /**
     * @return the x
     */
    public float getX() {
        return x;
    }

    /**
     * @param x the x to set
     */
    public void setX(float x) {
        this.x = x;
    }

    /**
     * @return the y
     */
    public float getY() {
        return y;
    }

    /**
     * @param y the y to set
     */
    public void setY(float y) {
        this.y = y;
    }

    /**
     * @return the r
     */
    public float getR() {
        return r;
    }

    /**
     * @param r the r to set
     */
    public void setR(float r) {
        this.r = r;
    }

    public Point getP() {
        return p;
    }

    public void setP(Point p) {
        this.p = p;
    }
    }

This is the CustomViewVisual1 class:

    public class CustomViewVisual1 extends View implements Runnable{
    int width;
    int height;
    int x = 0; 
    int y = 0;
    int canvasScale = 1;
    int countDraw = 0;

    Thread t = null;
    boolean isItOK = false;

    Paint paint;
    Particle[] p = new Particle[500];

    public CustomViewVisual1(Context context){
        super(context);
        initView();
    }


    public CustomViewVisual1(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    private void initView() {
        // TODO Auto-generated method stub
        paint = new Paint();

        Random r = new Random();
        for (int i = 0; i < p.length; i++) {
            p[i] = new Particle(width, r.nextFloat());
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        x = width/2;
        y = height/2;
        initView();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //canvas.translate(width/2, height/2);
        canvas.drawARGB(255, 0, 0, 0);
        canvas.scale(canvasScale, canvasScale);
        for (int i = 0; i < p.length; i++) {
            p[i].display(canvas, paint, canvasScale);
        }

        paint.setColor(Color.WHITE);
        canvas.drawCircle(x, y, 20, paint);
        /*
        paint.setColor(Color.CYAN);
        canvas.drawRect(0, 0, width, 10, paint);
        paint.setColor(Color.GREEN);
        canvas.drawRect(0, height-10, width, height, paint);
        paint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, 10, height, paint);
        paint.setColor(Color.MAGENTA);
        canvas.drawRect(width-10, 0, width, height, paint);
        */
        countDraw++;
        Log.d("How many draw = ", ""+countDraw);
    }


    public float getCanvasScale() {
        return canvasScale;
    }


    public void setCanvasScale(int canvasScale) {
        this.canvasScale = canvasScale;
    }

    public void pause() {
        isItOK = false;
        while (true) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            break;
        }
        t = null;
    }

    public void resume() {
        isItOK = true;
        t = new Thread(this);
        t.start();
    }


    @Override
    public void run() {
        // TODO Auto-generated method stub
    }
    }

This is the LayoutCanvas class:

    public class LayoutCanvas extends Activity implements OnTouchListener{

    RelativeLayout rl;
    SeekBar sb;
    CustomViewVisual1 cView;

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_layout_canvas);

        cView = new CustomViewVisual1(this);
        cView.setOnTouchListener(this);

        rl = (RelativeLayout) findViewById(R.id.lay_canvas);
        rl.setOnTouchListener(this);
        rl.setOnLongClickListener(new OnLongClickListener(){
            @Override
            public boolean onLongClick(View v) {
                // TODO Auto-generated method stub
                Log.d("on long touch", "keep touching");
                return false;
            }});

        sb = (SeekBar) findViewById(R.id.sb_activity_layout_canvas);
        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                Log.d("in progress changed", "before if() "+progress);
                if(progress > 0 && progress <=10){ 
                    Log.d("in progress changed", "in if() " + progress);
                    cView.setCanvasScale(progress);
                    cView.invalidate();
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }});

    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        Log.d("on touch", "Just touched");
        cView.x = (int) event.getX();
        cView.y = (int) event.getY();
        Log.d("cView x = ", ""+cView.x);
        Log.d("cView y = ", ""+cView.y);
        cView.invalidate();
        return false;
    }



    }

And this is the layout .xml file:

  <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:id="@+id/lay_canvas">

        <SeekBar
            android:id="@+id/sb_activity_layout_canvas"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/button1"
            android:layout_alignParentLeft="true"
            android:max="10" />

         <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@+id/button2"
            android:text="Button" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:text="Button" />

        <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_toRightOf="@+id/button2"
            android:text="Button" />

        <com.anvil.thesis.CustomViewVisual1
            android:id="@+id/myView1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/sb_activity_layout_canvas"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true" />

    </RelativeLayout>
Was it helpful?

Solution

Ok, the problem is that you have two instances of your custom view. The first is created when you call setContentView(R.layout.activity_layout_canvas); and you layout file is inflated. This instance is added to your view hierarchy. The second one you create explicitly with

cView = new CustomViewVisual1(this);
cView.setOnTouchListener(this);

This one, however, is never added to your view hierarchy. So when, in onTouch you call cView.invalidate(); it doesn't draw because cView is not actually on the screen.

What you need to do it make cView reference the custom view that was inflated with the layout like this:

cView = (CustomViewVisual1)findViewById(R.id.myView1);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top