Add custom view as a view of XML layout

2019-01-29 01:18发布

问题:

The scenario is the following:

I have an activity RunTrainingWorkoutsView that uses XML layout _run_workout.xml_ with some labels that are updated by CountDownTimer. Works fine...

Now, apart from labels that are updated every sec by onTick() callback method of CountDownTimer object I want to add a custom surface view to my _run_workout.xml layout_ that would draw some arcs updated by the same onTick() method every second...

my run_workout.xml:

<training.timer.CounterClockView 
    android:id="@+id/counter_clock_surface"
    android:layout_width="300dp" 
    android:layout_height="240dp">
</training.timer.CounterClockView>

My custom View extends surfaceView

public class CounterClockView extends SurfaceView {

Paint paint = new Paint();
Paint paint2 = new Paint();

final RectF rect = new RectF();
final RectF rect2 = new RectF();

int counterArcAngle = 15;
//constructor
public CounterClockView(Context context, AttributeSet attributeSet) {
    super(context);

    //setting some paint properties...

    this.setBackgroundColor(Color.TRANSPARENT);

}

@Override
public void onDraw(Canvas canvas) {

    rect.set(50, 50, 150, 150);
    rect2.set(50, 50, 150, 150);

    this.layout(0, 0, 200, 200);

    canvas.drawArc(rect, -90, 360, false, paint);
    canvas.drawArc(rect2, -90, counterArcAngle, false, paint2);

}

My main class that extends activity is getting the reference to a custom surfaceView in the layout with the following code:

//counterClockView  is declared outside of onCreate() as CounterClockView counterClockView;  

//later in onCreate(){....
counterClockView  = (CounterClockView) findViewById(R.id.counter_clock_surface);

The problem is changing the value of member variables of customView object (counterClockView)

counterClockView.counterArcAngle = 10;

will crash the app...

Also, from my main activity I would like to call invalidate() method to redo the surface view after changing the counterArcAngle value, but this causes app to crash too...

Why can't create counterClockView object and reference it to a xml layout element of the same type and the change its appereance, invalidate it etc. ?

EDIT LogCat:

threadid=1: thread exiting with uncaught exception (group=0x40015560)

ERROR/AndroidRuntime(487): FATAL EXCEPTION: main

ERROR/AndroidRuntime(487): java.lang.RuntimeException: Unable to start activity ComponentInfo{training.timer/training.timer.RunTrainingWorkoutsView}: java.lang.NullPointerException

ERROR/AndroidRuntime(487):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
ERROR/AndroidRuntime(487):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
ERROR/AndroidRuntime(487):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
ERROR/AndroidRuntime(487):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
ERROR/AndroidRuntime(487):     at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(487):     at android.os.Looper.loop(Looper.java:123)

ERROR/AndroidRuntime(487):     at android.app.ActivityThread.main(ActivityThread.java:3683)
ERROR/AndroidRuntime(487):     at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(487):     at java.lang.reflect.Method.invoke(Method.java:507)
ERROR/AndroidRuntime(487):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
ERROR/AndroidRuntime(487):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
ERROR/AndroidRuntime(487):     at dalvik.system.NativeStart.main(Native Method)
ERROR/AndroidRuntime(487): Caused by: java.lang.NullPointerException
ERROR/AndroidRuntime(487):     at training.timer.RunTrainingWorkoutsView.onCreate(RunTrainingWorkoutsView.java:72)
ERROR/AndroidRuntime(487):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
ERROR/AndroidRuntime(487):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
ERROR/AndroidRuntime(487):     ... 11 more

回答1:

I got it after 3 days of banging my head against the wall and by googling, stacOverflowing etc.

Actually, it was this stupid little thing...

My XML file where I defined the layout containing some usual android views (textView and buttons namely) and my custom view CounterClockView I had:

<training.timer.CounterClockView 
android:id="@+id/counter_clock_surface"
android:layout_width="300dp" 
android:layout_height="240dp">

where I had to have added one more line!

<training.timer.CounterClockView 
    xmlns:android="http://schemas.android.com/apk/res/android"   !!!
    android:id="@+id/counter_clock_surface"
    android:layout_width="300dp" 
    android:layout_height="240dp">
</training.timer.CounterClockView>

I have no idea why this namespace line made such a huge difference, but it works great!

Now, I can update my custom view from my main activity on every onTick() of CountDownTimer()...

The following answer was very helpful: findViewById() returns null for custom component in layout XML, not for other components



回答2:

Had the same issue, so I just implemented all the three constructors in the Java class of my custom view all the three constructors plus onDrow method and it worked like a charm. Try it.

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;

public class CustomWorldWideView extends View {

    public CustomWorldWideView(Context context) {

        super(context);
    }

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

    public CustomWorldWideView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //Some simple draw on the view...
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.parseColor("#FFA800"));


        Path path = new Path();

        path.moveTo(0, 0);
        path.lineTo(getWidth() / 2, 0);
        path.lineTo(getWidth(), getHeight()/2);
        path.lineTo(getWidth() / 2, getHeight());
        path.lineTo( 0, getHeight());
        path.lineTo( 0, 0);

        canvas.drawPath(path, paint);


    }
}

The XML :

        <PackageName.CustomWorldWideView
            android:layout_width="56.00dp"
            android:layout_height="43dp"
            android:id="@+id/world_wide_grid_view_2"
            />