0

I would like to extend TextView to implement a stopwatch function. The problem I got was a runtimeException concerning the inflating process.

03-30 12:57:28.749: E/AndroidRuntime(5700): java.lang.RuntimeException: Unable to start activity ComponentInfo{de.lipsksoft.apps.anwendung.t6student/de.lipsksoft.apps.anwendung.t6student.opsActivity}:
android.view.InflateException: Binary XML file line #29: Error inflating class de.lipsksoft.apps.anwendung.t6student.TimerView.t6student.TimerView

I did some changes because of the hints in here. My new code:

public class Timer extends TextView{

    private Handler handler = new Handler();
    private long startTime;
    private long elapsedTime;
    private final int REFRESH_RATE = 100;
    private String hours,minutes,seconds,milliseconds;
    private long secs,mins,hrs;
    private boolean stopped = false;

    Timer(Context context){
        super(context);
    }

    Timer (Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    Timer(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
    }

    public void start(){
        if(stopped){
            startTime = System.currentTimeMillis() - elapsedTime; 
        }
        else{
            startTime = System.currentTimeMillis();
        }
        handler.removeCallbacks(startTimer);
        handler.postDelayed(startTimer, 0);
    }

    public void stop(){
        handler.removeCallbacks(startTimer);
        stopped = true;
    }

    private Runnable startTimer = new Runnable() {
           public void run() {
               elapsedTime = System.currentTimeMillis() - startTime;
               updateTimer(elapsedTime);
               handler.postDelayed(this,REFRESH_RATE);
           }
        };

    private void updateTimer (float time){
        secs = (long)(time/1000);
        mins = (long)((time/1000)/60);
        hrs = (long)(((time/1000)/60)/60);

        /* Convert the seconds to String 
         * and format to ensure it has
         * a leading zero when required
         */
        secs = secs % 60;
        seconds=String.valueOf(secs);
    if(secs == 0){
        seconds = "00";
    }
    if(secs <10 && secs > 0){
        seconds = "0"+seconds;
    }

        /* Convert the minutes to String and format the String */

    mins = mins % 60;
        minutes=String.valueOf(mins);
    if(mins == 0){
        minutes = "00";
    }
    if(mins <10 && mins > 0){
        minutes = "0"+minutes;
    }

    /* Convert the hours to String and format the String */

    hours=String.valueOf(hrs);
    if(hrs == 0){
        hours = "00";
    }
    if(hrs <10 && hrs > 0){
        hours = "0"+hours;
    }

    /* Although we are not using milliseconds on the timer in this example
     * I included the code in the event that you wanted to include it on your own
     */
    milliseconds = String.valueOf((long)time);
    if(milliseconds.length()==2){
        milliseconds = "0"+milliseconds;
    }
    if(milliseconds.length()<=1){
        milliseconds = "00";
    }
        milliseconds = milliseconds.substring(milliseconds.length()-3, milliseconds.length()-2);

        /* Setting the timer text to the elapsed time */
        this.setText(minutes + ":" + seconds + "." + milliseconds);
        //activity.((TextView)findViewById(R.id.timerMs)).setText("." + milliseconds);
    }
}

XML:

 <de.lipsksoft.apps.anwendung.t6student.Timer
        android:id="@+id/timer1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TimerView" />

But I got still the sam error. Do you have any idea what goes wrong?

Thank you for your help.

Peter Panne
  • 425
  • 1
  • 4
  • 15

1 Answers1

1

You need to have these constructors

Android Custom View Constructor

public TimerView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public TimerView (Context context) {
super(context);
}
public TimerView (Context context, AttributeSet attrs) {
super(context, attrs);
}

Also you have

this.setText(milliSeconds + ":" + seconds + ":" + minutes);

You cannot update ui from a non ui thread.

Read

http://developer.android.com/guide/components/processes-and-threads.html

Edit:

What you have

public class Timer extends TextView{

Then in xml you have

<de.lipsksoft.apps.anwendung.t6student.TimerView // TimerView is not the same as Timer

Edit 2:

In Activity

TimerView  tv = (TimerView) this.findViewById(R.id.timer1);
tv.start();

Then in xml

My package name is different

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
<com.example.testall.TimerView
        android:id="@+id/timer1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TimerView" />
</RelativeLayout>

Then

public class TimerView extends TextView{

    private Handler handler = new Handler();
    private long startTime;
    private long elapsedTime;
    private final int REFRESH_RATE = 100;
    private String hours,minutes,seconds,milliseconds;
    private long secs,mins,hrs;
    private boolean stopped = false;

    public TimerView(Context context){
        super(context);
        //start();
    }

    public TimerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //start();
    }

    public TimerView(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        //start();
    }

    public void start(){
        if(stopped){
            startTime = System.currentTimeMillis() - elapsedTime; 
        }
        else{
            startTime = System.currentTimeMillis();
        }
        handler.removeCallbacks(startTimer);
        handler.postDelayed(startTimer, 0);
    }

    public void stop(){
        handler.removeCallbacks(startTimer);
        stopped = true;
    }

    private Runnable startTimer = new Runnable() {
           public void run() {
               elapsedTime = System.currentTimeMillis() - startTime;
               updateTimer(elapsedTime);
               handler.postDelayed(this,REFRESH_RATE);
           }
        };

    private void updateTimer (float time){
        secs = (long)(time/1000);
        mins = (long)((time/1000)/60);
        hrs = (long)(((time/1000)/60)/60);

        /* Convert the seconds to String 
         * and format to ensure it has
         * a leading zero when required
         */
        secs = secs % 60;
        seconds=String.valueOf(secs);
    if(secs == 0){
        seconds = "00";
    }
    if(secs <10 && secs > 0){
        seconds = "0"+seconds;
    }

        /* Convert the minutes to String and format the String */

    mins = mins % 60;
        minutes=String.valueOf(mins);
    if(mins == 0){
        minutes = "00";
    }
    if(mins <10 && mins > 0){
        minutes = "0"+minutes;
    }

    /* Convert the hours to String and format the String */

    hours=String.valueOf(hrs);
    if(hrs == 0){
        hours = "00";
    }
    if(hrs <10 && hrs > 0){
        hours = "0"+hours;
    }

    /* Although we are not using milliseconds on the timer in this example
     * I included the code in the event that you wanted to include it on your own
     */
    milliseconds = String.valueOf((long)time);
    if(milliseconds.length()==2){
        milliseconds = "0"+milliseconds;
    }
    if(milliseconds.length()<=1){
        milliseconds = "00";
    }
        milliseconds = milliseconds.substring(milliseconds.length()-3, milliseconds.length()-2);

        /* Setting the timer text to the elapsed time */
        this.setText(minutes + ":" + seconds + "." + milliseconds);
        //activity.((TextView)findViewById(R.id.timerMs)).setText("." + milliseconds);
    }
}

Snap

enter image description here

Community
  • 1
  • 1
Raghunandan
  • 129,147
  • 24
  • 216
  • 249
  • Yea sorry...thats not the problem. I just forgot to update the xml part. In my actual code it is right. – Peter Panne Mar 30 '14 at 18:40
  • @PeterPanne that would have been a problem if that was not wrong. and you need to call the `start()` in all the constructors and rename Timer to something else – Raghunandan Mar 30 '14 at 18:46
  • Why should I call start in all constructors? I do it from my activity because I want to react on events (button click etc.). I renamed it to NewTimer. Still the same error. – Peter Panne Mar 30 '14 at 18:53
  • @PeterPanne coz you don't know which constructor is used. – Raghunandan Mar 30 '14 at 18:55
  • Could it be that the handler has to be created in the activity? – Peter Panne Mar 30 '14 at 19:02
  • @PeterPanne Your problem has nothing to do with excpetion – Raghunandan Mar 30 '14 at 19:03
  • @PeterPanne Do check the edit 2. Only Package name is different rest all same and it works. rest it upto you to figure out. Handelr has nothing to with the error you got – Raghunandan Mar 30 '14 at 19:07
  • The error remains since the "NewTimer" is in the XML indepetent whether I use this class in the code or not. – Peter Panne Mar 30 '14 at 19:08
  • @PeterPanne i can post a example i can say what's wrong its upto you to fix good luck – Raghunandan Mar 30 '14 at 19:09
  • I found the mistake. You were on the right track. The constructors cause the problem. You have to write "public" in front of that. Usually you don't, but when you extend views you must. So you gave me the right hint but I didn't know that the "public" was so important. – Peter Panne Mar 31 '14 at 21:02
  • @PeterPanne it should be public bcoz you need the constructor accessed by the android framework and by default its not public so you need public – Raghunandan Apr 01 '14 at 05:05