2012-01-13 14 views
37

कोड:एंड्रॉइड: CountDownTimer आखिरी बार टिक() पर छोड़ देता है!

public class SMH extends Activity { 

    public void onCreate(Bundle b) { 
     super.onCreate(b); 
     setContentView(R.layout.main); 

     TextView tv = (TextView) findViewById(R.id.tv); 

     new CountDownTimer(10000, 2000) { 
      public void onTick(long m) { 
       long sec = m/1000+1; 
       tv.append(sec+" seconds remain\n"); 
      } 
      public void onFinish() { 
       tv.append("Done!"); 
      } 
     }.start(); 
    } 

आउटपुट:
10 सेकंड रहने
8 सेकंड रहने
6 सेकंड रहने
4 सेकंड
हो गया रहने!

समस्या:

मैं इसे कैसे दिखाने के लिए "2 सेकंड रहने" मिलता है? समय बीत चुका है वास्तव में 10 सेकंड है, लेकिन आखिरी ऑन टिक() कभी नहीं होता है।

10 सेकंड रहने
9 सेकंड रहने
8 सेकंड रहने
7 सेकंड रहने
6 सेकंड रहने
5 सेकंड
रहते हैं: अगर मैं 2000 से 1000 तक दूसरा पैरामीटर बदलने के लिए, तो यह उत्पादन होता है 4 सेकंड
3 सेकंड रहें
2 सेकंड
हो गया!

तो आप देखते हैं, ऐसा लगता है कि यह आखिरी ऑन टिक() कॉल छोड़ रहा है। और बीटीडब्ल्यू, एक्सएमएल फ़ाइल मूल रूप से डिफ़ॉल्ट मुख्य.एक्सएमएल है जिसमें टेक्स्ट व्यू आईडी टीवी और पाठ को "" सेट किया गया है।

उत्तर

21

मुझे नहीं पता कि आखिरी टिक क्यों काम नहीं कर रही है, लेकिन उदाहरण के लिए आप अपना खुद का टाइमर Runable बना सकते हैं।

class MyCountDownTimer { 
    private long millisInFuture; 
    private long countDownInterval; 
    public MyCountDownTimer(long pMillisInFuture, long pCountDownInterval) { 
      this.millisInFuture = pMillisInFuture; 
      this.countDownInterval = pCountDownInterval; 
     } 
    public void Start() 
    { 
     final Handler handler = new Handler(); 
     Log.v("status", "starting"); 
     final Runnable counter = new Runnable(){ 

      public void run(){ 
       if(millisInFuture <= 0) { 
        Log.v("status", "done"); 
       } else { 
        long sec = millisInFuture/1000; 
        Log.v("status", Long.toString(sec) + " seconds remain"); 
        millisInFuture -= countDownInterval; 
        handler.postDelayed(this, countDownInterval); 
       } 
      } 
     }; 

     handler.postDelayed(counter, countDownInterval); 
    } 
} 

और इसे शुरू करने,

new MyCountDownTimer(10000, 2000).Start(); 

संपादित नासमझ है प्रश्न

के लिए आप एक चर काउंटर स्थिति (बुलियन) धारण करने के लिए होना चाहिए। तो आप स्टार्ट() जैसे स्टॉप() विधि लिख सकते हैं।

संपादित -2 के लिए नासमझ है प्रश्न

वास्तव में वहाँ काउंटर रोक पर कोई बग है लेकिन शुरुआत पर एक बग के बाद बंद (फिर से शुरू) फिर से नहीं है।

मैं एक नया अद्यतन पूर्ण कोड लिख रहा हूं जिसे मैंने अभी कोशिश की थी और यह काम कर रहा है। यह एक मूल काउंटर है जो स्क्रीन पर स्टार्ट और स्टॉप बटन के साथ समय दिखाता है।

काउंटर के लिए वर्ग

public class MyCountDownTimer { 
    private long millisInFuture; 
    private long countDownInterval; 
    private boolean status; 
    public MyCountDownTimer(long pMillisInFuture, long pCountDownInterval) { 
      this.millisInFuture = pMillisInFuture; 
      this.countDownInterval = pCountDownInterval; 
      status = false; 
      Initialize(); 
    } 

    public void Stop() { 
     status = false; 
    } 

    public long getCurrentTime() { 
     return millisInFuture; 
    } 

    public void Start() { 
     status = true; 
    } 
    public void Initialize() 
    { 
     final Handler handler = new Handler(); 
     Log.v("status", "starting"); 
     final Runnable counter = new Runnable(){ 

      public void run(){ 
       long sec = millisInFuture/1000; 
       if(status) { 
        if(millisInFuture <= 0) { 
         Log.v("status", "done"); 
        } else { 
         Log.v("status", Long.toString(sec) + " seconds remain"); 
         millisInFuture -= countDownInterval; 
         handler.postDelayed(this, countDownInterval); 
        } 
       } else { 
        Log.v("status", Long.toString(sec) + " seconds remain and timer has stopped!"); 
        handler.postDelayed(this, countDownInterval); 
       } 
      } 
     }; 

     handler.postDelayed(counter, countDownInterval); 
    } 
} 

गतिविधि वर्ग

public class CounterActivity extends Activity { 
    /** Called when the activity is first created. */ 
    TextView timeText; 
    Button startBut; 
    Button stopBut; 
    MyCountDownTimer mycounter; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     timeText = (TextView) findViewById(R.id.time); 
     startBut = (Button) findViewById(R.id.start); 
     stopBut = (Button) findViewById(R.id.stop); 
     mycounter = new MyCountDownTimer(20000, 1000); 
     RefreshTimer(); 
    } 

    public void StartTimer(View v) { 
     Log.v("startbutton", "saymaya basladi"); 
     mycounter.Start(); 
    } 

    public void StopTimer(View v) { 
     Log.v("stopbutton", "durdu"); 
     mycounter.Stop(); 
    } 

    public void RefreshTimer() 
    { 
     final Handler handler = new Handler(); 
     final Runnable counter = new Runnable(){ 

      public void run(){ 
       timeText.setText(Long.toString(mycounter.getCurrentTime())); 
       handler.postDelayed(this, 100); 
      } 
     }; 

     handler.postDelayed(counter, 100); 
    } 
} 

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" 
    android:weightSum="1"> 
    <TextView android:textAppearance="?android:attr/textAppearanceLarge" 
       android:text="TextView" android:layout_height="wrap_content" 
       android:layout_width="wrap_content" 
       android:id="@+id/time"> 
    </TextView> 
    <Button android:text="Start" 
      android:id="@+id/start" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:onClick="StartTimer"> 
    </Button> 
    <Button android:text="Stop" 
      android:id="@+id/stop" 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:onClick="StopTimer"> 
    </Button> 
</LinearLayout> 
+1

मुझे लगा कि मुझे इसकी देखभाल करने के लिए केवल एक हैंडलर का उपयोग करना चाहिए, लेकिन मेरे प्रश्न में जिस दोष के बारे में बात कर रहा हूं, उससे कम, काउंटरडाउन टाइमर इतना आसान और सही है! मेरा मतलब सिर्फ आपके कोड उदाहरण और मेरा है, आप देख सकते हैं कि कितना सरल और कॉम्पैक्ट काउंटरडाउन टाइमर की तुलना हैंडलर से की जाती है! अगर केवल वास्तव में यह सही काम करता है हालांकि। – ProgrammingMakesMeQQ

+3

बिल्कुल आप सही हैं, मैं बस यह दिखाना चाहता हूं कि आप इसे वैकल्पिक तरीकों से कर सकते हैं। – ocanal

+0

@ocanal धन्यवाद मैं आपके कोड का उपयोग कर रहा हूं लेकिन अगर मैं टाइमर को कैसे रोकना चाहता हूं ... कृपया – Goofy

0

आप calcula हैं टिंग समय गलत तरीके से शेष है। कॉलबैक कार्य पूरा होने तक मिलीसेकंड की संख्या प्राप्त करता है।

public void onTick(long m) { 
    long sec = m/1000+1; 
    tv.append(sec+" seconds remain\n"); 
} 

होना चाहिए

public void onTick(long m) { 
    long sec = m/1000; 
    tv.append(sec+" seconds remain\n"); 
} 

मैं इस वर्ग अपने आप इस्तेमाल कभी नहीं किया है, लेकिन यह है कि आप एक कॉलबैक तत्काल यह शुरू होता है, जो नहीं मिलेगा क्यों ऐसा लगता है जैसे आप एक भूल रहे हैं लग रहा है प्रवेश। जैसे 10000 एमएस, 1000 एमएस प्रति टिक आपको कुल 9 अपडेट कॉलबैक मिलेगा, 10 - 9 000, 8000, 7000, 6000, 5000, 4000, 3000, 2000, 1000, खत्म नहीं होगा।

+2

मैंने आउटपुट को समझने के लिए +1 किया था। मेरे प्रश्न में जो आउटपुट है, वह उलटी गिनती खत्म होने तक वास्तविक समय के प्रतिबिंबित है। जब यह कहता है "3 सेकंड ... 2 सेकंड ... हो गया!" इसका मतलब है कि "हो गया!" से पहले वास्तव में 2 सेकंड शेष थे संदेश दिखाया गया। तो मूल रूप से यदि मैं "long sec = m/1000" से "+1" निकालता हूं तो पाठ "2 सेकंड ... 1 सेकंड ... हो गया!" भले ही यह "1 सेकंड" कहता है, वास्तव में जाने के लिए 2 सेकंड हैं। यह थोड़ा उलझन में लगता है लेकिन यदि आप मेरा संक्षिप्त कोड आज़माते हैं, तो आप देखेंगे कि मैं किस बारे में बात कर रहा हूं। – ProgrammingMakesMeQQ

3

मैंने इस समस्या को समझने के लिए घंटों बिताए हैं, और मैं आपको एक अच्छा काम दिखाने में प्रसन्न हूं। onFinish() कॉल के लिए प्रतीक्षा करने से परेशान न हों, बस अपनी इकाइयों में 1 (या जो भी अंतराल है) जोड़ें, फिर onTick() कॉल में कोई कथन जोड़ें। अंतिम onTick() पर बस अपना onFinish() कार्य करें। यहां मुझे यह मिला है:

new CountDownTimer((countDownTimerValue + 1) * 1000, 1000) { //Added 1 to the countdownvalue before turning it into miliseconds by multiplying it by 1000. 
     public void onTick(long millisUntilFinished) { 

      //We know that the last onTick() happens at 2000ms remaining (skipping the last 1000ms tick for some reason, so just throw in this if statement. 
      if (millisUntilFinished < 2005){ 
       //Stuff to do when finished. 
      }else{ 
       mTextField.setText("Time remaining: " + (((millisUntilFinished)/1000) - 1)); //My textfield is obviously showing the remaining time. Note how I've had to subtrack 1 in order to display the actual time remaining. 
      } 
     } 

     public void onFinish() { 
     //This is when the timer actually finishes (which would be about 1000ms later right? Either way, now you can just ignore this entirely. 


     } 
    }.start(); 
+0

मुझे आपको समाधान पसंद है लेकिन मुझे लगता है कि कम से कम एंड्रॉइड> = 6 के लिए, समस्या अंतिम पर टिक() नहीं है, लेकिन ऑनफिनिश() अभी भी सही समय पर बुलाया जा रहा है। चूंकि मैं अपने ऑनटिक में केवल उलटी गिनती डिस्प्ले अपडेट कर रहा हूं, इसलिए मैं केवल अंतिम अपडेट दिखाने के लिए अपने समाधान का उपयोग करता हूं लेकिन उसी स्थान पर ऑनफिनिश() एनिमेशन छोड़ देता हूं – rockhammer

3

जबकि उपरोक्त समाधान मान्य है, इसे और भी बेहतर किया जा सकता है। यह अनावश्यक रूप से किसी अन्य वर्ग के अंदर एक चलने योग्य है (जिसका पहले से ही इसका इलाज किया जा सकता है)। तो बस एक वर्ग बनाएं जो थ्रेड (या रननेबल) बढ़ाता है।

class MyTimer extends Thread { 
     private long millisInFuture; 
     private long countDownInterval; 
     final Handler mHandler = new Handler(); 

     public MyTimer(long pMillisInFuture, long pCountDownInterval) { 
     this.millisInFuture = pMillisInFuture; 
     this.countDownInterval = pCountDownInterval; 
     } 

     public void run() { 
     if(millisInFuture <= 0) { 
      Log.v("status", "done"); 
     } else { 
      millisInFuture -= countDownInterval; 
      mHandler.postDelayed(this, countDownInterval); 
     } 
     } 
    } 
2

सबसे सरल उपाय मैं के साथ आया था इस प्रकार है। ध्यान दें कि यह केवल तभी काम करता है जब आपको सेकंड उलटी गिनती के साथ प्रदर्शित करने के लिए एक साधारण स्क्रीन की आवश्यकता होती है।

mTimer = new CountDownTimer(5000, 100){ 
      public void onTick(long millisUntilFinished) { 
       mTimerView.setText(Long.toString(millisUntilFinished/1000));     
      } 

      public void onFinish() { 
       mTimerView.setText("Expired"); 
      } 
     }; 

     mTimer.start(); 

() onTick उपरोक्त कोड में हर 100 मिलीसेकंड कहा जाता है लेकिन देखने में केवल कुछ सेकंड प्रदर्शित होते हैं।

42

मैंने काउंटरडाउन टाइमर के स्रोत कोड की जांच की। "लापता टिक" काउंटडाउन टिमर की एक विशेष विशेषता से आता है कि मैंने अभी तक कहीं और दस्तावेज नहीं देखा है:

प्रत्येक टिक की शुरुआत में, टिक() से पहले, उलटी गिनती के अंत तक शेष समय है गणना की। यदि यह समय उलटी गिनती समय अंतराल से छोटा है, तो टिक नहीं कहा जाता है। इसके बजाय केवल अगले टिक (जहां ऑनफिनिश() विधि कहा जाएगा) निर्धारित है।

तथ्य यह है कि हार्डवेयर घड़ियों नहीं हमेशा की तरह, सुपर सटीक हैं वहाँ पृष्ठभूमि में अन्य प्रक्रियाओं है कि धागा CountDownTimer चल देरी हो सकता है कि यह देखते हुए प्लस एंड्रॉयड ही शायद एक छोटा सा विलंब CountDownTimer का संदेश हैंडलर बुला यह है पैदा करेगा कि संभावना से अधिक है कि गिनती के अंत से पहले आखिरी टिक के लिए कॉल कम से कम एक मिलीसेकंद देर हो जाएगी और इसलिए टिक() पर कॉल नहीं किया जाएगा।

अपने आवेदन के लिए मैं बस टिक अंतराल "थोड़ा" छोटे (500 एमएस)

myCountDownTimer = new CountDownTimer(countDownTime, intervalTime - 500) { 
            ... 
    } 

बनाकर इस समस्या का समाधान और मैं अपने कोड छोड़ सकता यह है बस के रूप में। उन अनुप्रयोगों के लिए जहां अंतराल समय की लंबाई महत्वपूर्ण है, यहां पोस्ट किए गए अन्य समाधान शायद सबसे अच्छे हैं।

+1

हां, वैकल्पिक रूप से आप "countDownTime + 900" या ऐसा कुछ कह सकते हैं । 10000 के उलटी गिनती के समय और 1000 के अंतराल के साथ आप केवल 10 टिक देखेंगे यदि प्रत्येक टिक ठीक समय पर आग लगती है। इस उदाहरण में 900 एमएमएस जोड़ने से यह 1 और टिक के लिए पर्याप्त समय नहीं देगा, लेकिन घड़ी के ठीक समय पर नहीं होने के कारण कुछ मार्जिन के साथ "आखिरी" टिक के लिए पर्याप्त समय लगेगा। (उदाहरण के लिए, जब अंतिम टिक प्रारंभ से "10004" एमएस पर हिट हो जाती है, तो आप इसे याद करेंगे, लेकिन अगर आपने थोड़ा पैडिंग जोड़ा है, तो यह ठीक होगा) – JamieB

+0

टिक को 1000ms से 500ms तक बदलकर यह मेरे लिए भी तय किया गया है। – rsa

+0

यह मेरे लिए काम करता है! बहुत बहुत धन्यवाद!! –

2

मुझे आसान समाधान मिला। मैं ProgressBar अद्यतन के लिए उलटी गिनती की जरूरत है, तो मैं ऐसा किया:

new CountDownTimer(1000, 100) { 

    private int counter = 0; 

    @Override 
    public void onTick(long millisUntilFinished) { 
     Log.d(LOG_TAG, "Tick: " + millisUntilFinished); 
     if (++counter == 10) { 
      timeBar.setProgress(--lenght); // timeBar and lenght defined in calling code 
      counter = 0; 
     } 
    } 


    @Override 
    public void onFinish() { 
     Log.d(LOG_TAG, "Finish."); 

     timeBar.setProgress(0); 
    } 

}; 

छोटे टिक चाल करना :)

+0

काम करता है, धन्यवाद –

2

तो मुझे लगता है मैं उपयोग करने के बजाय बोर्ड से कुछ ज्यादा हो गया था क्योंकि मेरी घड़ी अपनी ही धागे में चलता है पोस्टडे हैंडलर, हालांकि यह हमेशा उस थ्रेड पर पोस्ट करता है जो इसे बनाया गया था। मुझे यह भी पता था कि मुझे केवल सेकंड की परवाह है ताकि इस विचार के आसपास सरलीकृत हो। यह आपको इसे रद्द करने और इसे पुनरारंभ करने देता है। मैंने इसमें रोका नहीं है क्योंकि यह मेरी ज़रूरतों में नहीं है।

/** 
* Created by MinceMan on 8/2/2014. 
*/ 
public abstract class SecondCountDownTimer { 

private final int seconds; 
private TimerThread timer; 
private final Handler handler; 

/** 
* @param secondsToCountDown Total time in seconds you wish this timer to count down. 
*/ 
public SecondCountDownTimer(int secondsToCountDown) { 
    seconds = secondsToCountDown; 
    handler = new Handler(); 
    timer = new TimerThread(secondsToCountDown); 
} 

/** This will cancel your current timer and start a new one. 
* This call will override your timer duration only one time. **/ 
public SecondCountDownTimer start(int secondsToCountDown) { 
    if (timer.getState() != State.NEW) { 
     timer.interrupt(); 
     timer = new TimerThread(secondsToCountDown); 
    } 
    timer.start(); 
    return this; 
} 

/** This will cancel your current timer and start a new one. **/ 
public SecondCountDownTimer start() { 
    return start(seconds); 
} 

public void cancel() { 
    if (timer.isAlive()) timer.interrupt(); 
    timer = new TimerThread(seconds); 
} 

public abstract void onTick(int secondsUntilFinished); 
private Runnable getOnTickRunnable(final int second) { 
    return new Runnable() { 
     @Override 
     public void run() { 
      onTick(second); 
     } 
    }; 
} 

public abstract void onFinish(); 
private Runnable getFinishedRunnable() { 
    return new Runnable() { 
     @Override 
     public void run() { 
      onFinish(); 
     } 
    }; 
} 

private class TimerThread extends Thread { 

    private int count; 

    private TimerThread(int count) { 
     this.count = count; 
    } 

    @Override 
    public void run() { 
     try { 
      while (count != 0) { 
       handler.post(getOnTickRunnable(count--)); 
       sleep(1000); 
      } 
     } catch (InterruptedException e) { } 
     if (!isInterrupted()) { 
      handler.post(getFinishedRunnable()); 
     } 
    } 
} 

}

1

Nantoka के जवाब पर विस्तार करने के लिए। यहाँ दृश्य सुनिश्चित करने के लिए मेरे कोड सही ढंग से अद्यतन किया जाता है है:

countDownTimer = new CountDownTimer(countDownMsec, 500) 
{ 
    public void onTick(long millisUntilFinished) 
    { 
     if(millisUntilFinished!=countDownMsec) 
     { 
      completedTick+=1; 
      if(completedTick%2==0)  // 1 second has passed 
      { 
       // UPDATE VIEW HERE based on "seconds = completedTick/2" 
      } 
      countDownMsec = millisUntilFinished; // store in case of pause 
     } 
    } 

    public void onFinish() 
    { 
     countDownMsec = 0; 
     completedTick+=2;  // the final 2 ticks arrive together 
     countDownTimer = null; 

     // FINAL UPDATE TO VIEW HERE based on seconds = completedTick/2 == countDownMsec/1000 
    } 
} 
0

मैं भी CountDownTimer के साथ एक ही मुद्दा का सामना करना पड़ा है और मैं अलग दृष्टिकोण की कोशिश की। तो सबसे आसान तरीकों में से एक @Nantoca द्वारा प्रदान किए गए समाधान में है - वह आवृत्ति को 1000ms से 500ms तक दोगुना करने का सुझाव देता है। लेकिन मुझे इस समाधान को पसंद नहीं है क्योंकि यह अधिक काम करता है जो कुछ अतिरिक्त बैटरी संसाधन का उपभोग करेगा।

इसलिए मैंने @ ओकनल की आत्मा का उपयोग करने और अपना खुद का सरल कस्टमकाउंटडाउन टाइमर लिखने का निर्णय लिया।

  1. यह थोड़ा अक्षम (दूसरा हैंडलर बनाने परिणाम प्रकाशित करने के लिए)

  2. यह देरी से पहला परिणाम प्रकाशित करने के लिए शुरू होता है है:

    लेकिन मैं अपने कोड में खामियों की जोड़ी मिल गया। (आपको पहली शुरुआत के दौरान postDelayed() के बजाय post() विधि करने की आवश्यकता है)

  3. अजीब लग रहा है। पूंजी पत्र के साथ तरीके, क्लासिक के बजाय स्थिति रद्द किया गया बूलियन और कुछ अन्य।

तो मैं इसे थोड़ा साफ किया और यहाँ अपने दृष्टिकोण के अधिक सामान्य संस्करण है:

private class CustomCountDownTimer { 

    private Handler mHandler; 
    private long millisUntilFinished; 
    private long countDownInterval; 
    private boolean isCanceled = false; 

    public CustomCountDownTimer(long millisUntilFinished, long countDownInterval) { 
     this.millisUntilFinished = millisUntilFinished; 
     this.countDownInterval = countDownInterval; 
     mHandler = new Handler(); 
    } 

    public synchronized void cancel() { 
     isCanceled = true; 
     mHandler.removeCallbacksAndMessages(null); 
    } 

    public long getRemainingTime() { 
     return millisUntilFinished; 
    } 

    public void start() { 

     final Runnable counter = new Runnable() { 

      public void run() { 

       if (isCanceled) { 
        publishUpdate(0); 
       } else { 

        //time is out 
        if(millisUntilFinished <= 0){ 
         publishUpdate(0); 
         return; 
        } 

        //update UI: 
        publishUpdate(millisUntilFinished); 

        millisUntilFinished -= countDownInterval; 
        mHandler.postDelayed(this, countDownInterval); 
       } 
      } 
     }; 

     mHandler.post(counter); 
    } 
} 
0

यदि आपका समय अंतराल 4 से अधिक सेकंड है तो हर onTick() कॉल नहीं उचित होगा। तो यदि आप सटीक परिणाम चाहते हैं तो 5 सेकंड से कम अंतराल रखें। onTick() से पहले, प्रत्येक टिक की शुरुआत में रिसायन कहा जाता है, उलटी गिनती के अंत तक शेष समय की गणना की जाती है और यदि यह समय उलटी गिनती समय अंतराल से छोटा है, तो onTick() अब और नहीं बुलाया जाएगा। इसके बजाय केवल अगले टिक (जहां onFinish() विधि कहा जाएगा) निर्धारित है।

संबंधित मुद्दे