安卓:在处理程序,并可以运行一个ListView多个定时器。问题2 [英] Android: Multiple timers in a ListView with handler and runnable. 2 Problems

查看:136
本文介绍了安卓:在处理程序,并可以运行一个ListView多个定时器。问题2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建一个包含2列一个ListView的应用程序。在第一列中的倒数应显示和在第二列的附加文本,解释什么是倒计时。
下面你看到我的code,它...作品或多或少。
我有带计时器的滴答多行列表视图。
一个问题是:
在set.Text()在我的可运行似乎覆盖所有行。例如。可运行用于行1套文本也行到第2和第3,可运行为行2台文本也为1和3等。这具有的第一列闪烁的效果(用正确的值和其他行的值)。
我怎样才能在一个列表视图设置一个特定行的文字?

下一个问题是:
可运行在上和即使我从处理程序删除回调运行。但是,当活动是背景或关闭不需要的计时器滴答声,我不想浪费系统资源。

我的活动:

 公共类TimerActivity扩展ListActivity {MyTimerAdapter myTimerAdapter = NULL;
ArrayList的<龙> timerList =新的ArrayList<龙>();
ArrayList的<串GT;文本清单=新的ArrayList<串GT;();@覆盖
公共无效的onCreate(捆绑savedInstanceState){
    super.onCreate(savedInstanceState);
    的setContentView(R.layout.listactivity);    myTimerAdapter =新MyTimerAdapter(这一点,R.layout.row,R.id.tv_timer,R.id.tv_text);
    setListAdapter(myTimerAdapter);
}@覆盖
保护无效onResume(){
    super.onResume();
    refreshView();
}@覆盖
保护无效的onPause(){
    myTimerAdapter.clear();
    myTimerAdapter.notifyDataSetChanged();
    super.onPause();
}私人无效refreshView(){    myTimerAdapter.clear();
    timerList.clear();
    textList.clear();    //一些code读取数据库并填写
    //阵列timerList用long值(用于显示倒计时)
    //和文本清单与一些附加文本    myTimerAdapter.add(timerList,文本清单);
    myTimerAdapter.notifyDataSetChanged();
}}

我的适配器:

 公共类MyTimerAdapter扩展ArrayAdapter<串GT; {
私人活动mContext;
私人的ArrayList<龙> mTimer;
私人的ArrayList<串GT;多行文字;
私人诠释mViewId;
私人诠释mViewIdFieldTimer;
私人诠释mViewIdFieldText;
私人诠释LISTSIZE;
私人的ArrayList<处理器> handlerList =新的ArrayList<处理器>();
私人的ArrayList< TimerRunnable>运行列表=新的ArrayList< TimerRunnable>();公共MyTimerAdapter(活动的背景下,INT textViewResId,诠释TV1,TV2 INT){
    超(背景下,textViewResId);
    mContext =背景;
    mViewId = textViewResId;
    mViewIdFieldTimer = TV1;
    mViewIdFieldText = TV2;
    LISTSIZE = 0;
}公共无效添加(ArrayList的<龙>定时器,ArrayList的<弦乐>文字){
    mTimer =定时器;
    多行文字=文本;
    LISTSIZE = mText.size();
    handlerList.clear();
    runList.clear();
}@覆盖
公共无效清除(){
    super.clear();
    INT I;
    对于(i = 0; I< LISTSIZE;我++){
        handlerList.get(ⅰ).removeCallbacksAndMessages(runList.get(ⅰ));
        runList.get(ⅰ).stopHandler();
    }
}@覆盖
公众诠释的getCount(){
    返回LISTSIZE;
}@覆盖
公共查看getView(INT位置,查看convertView,父母的ViewGroup){
    视图V = convertView;
    如果(V == NULL){
        LayoutInflater VI = mContext.getLayoutInflater();
        V = vi.inflate(mViewId,NULL);
    }    长timerLine = mTimer.get(位置);
    如果(timerLine!= 0){
        TextView的tvTimer =(TextView中)v.findViewById(mViewIdFieldTimer);        如果(tvTimer!= NULL){
            tvTimer.setTag(位置);
            最后的处理程序mTimerHandler =新的处理程序();
            TimerRunnable的TimerTask =新TimerRunnable(tvTimer,tvTimer.getTag()的toString(),timerLine。);
            mTimerHandler.post(TimerTask的);            //保存阵列后停止
            handlerList.add(mTimerHandler);
            runList.add(TimerTask的);
        }
    }    字符串TEXTLINE = mText.get(位置);
    如果(TEXTLINE!= NULL){
        TextView的tvText =(TextView中)v.findViewById(mViewIdFieldText);
        如果(tvText!= NULL){
            tvText.setText(TEXTLINE);
        }
    }    返回伏;
}
}

我的Runnable:

 公共类TimerRunnable实现Runnable {
私人TextView的电视;
最后的处理程序mTimerHandler =新的处理程序();
字符串标记;
长结束时间;
长秒;公共TimerRunnable(TextView的电视,串标,长结束时间){
    this.tv =电视;
    this.tag =标签;
    this.endtime =结束时间;
}公共无效的run(){
    如果(tv.getTag()。的toString()。等于(标签)){
        日历CAL = Calendar.getInstance();
        秒=结束时间 - (cal.getTimeInMillis()/ 1000); //结束时间 - aktuelle时代周报
        如果(SEC> = 0){            //一些code格式化时间,以秒为类似HH:MM:SS(VAR字符串TXT)            tv.setText(TXT);            的System.out.println(TXT); //只为试验;所以我可以看到,可运行仍在运行            mTimerHandler.postDelayed(这一点,1000);
        }
    }
}公共无效stopHandler(){
    mTimerHandler.removeCallbacksAndMessages(NULL);
}
}


解决方案

我会做如下:


  1. 使用计时器
    (<一href=\"http://developer.android.com/reference/java/util/Timer.html\">http://developer.android.com/reference/java/util/Timer.html)和
    让它运行周期每秒或什么的。

  2. 创建活动的onCreate Timer对象()和取消()计时器在活动的onDestroy()(甚至更好的onResume启动它,并在onPause取消它())。所以这就是如何避免内存泄漏和你的计时器运行,甚至如果活动被关闭最简单的方法。

  3. 更​​新从正在运行的计时器ListView控件。有2个选项:


    • 只要从计时器调用 adapter.notifyDatasetChanged()。但是这可能导致闪烁的列表视图。然而,你可以尝试和检验,如果这可以在应用程序中工作,因为是最简单的实现。

    • 更​​新直接当前可见的ListView中TextViews

       私人无效录入(){INT firstVisibleItemIndex = listView.getFirstVisiblePosition();的for(int i = 0; I&LT; listView.getChildCount();我++){
          视图V = listView.getChildAt(I)        YourItem项目=(YourItem)适配器
                      .getItem(firstVisibleItemIndex + I));        ViewHolder VH =(ViewHolder)v.getTag();
                                  //计算时间不知何故,即所谓的methot上的数据项
                                  vh.tvTimer.setText(item.getElapsedTime());
          }}

      }



所以我想第二个选项将是最好的。你可能不知道ViewHolder是什么。 ViewHolder是增加你的ListView的性能的图形。 <一href=\"http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder\">http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder
适配器的问题是,findViewById()将被称为每次用户滚动页面。 findViewById()是一个昂贵的方法调用,因为它必须遍历所有孩子的观点找到一个给定的ID。在简单的适配器,可能没有太大的影响(因为你只有一个子视图,TextView的)。但ViewHolder是你应该在你的适配器实现,总是用

I'm creating an app that contains a ListView with 2 columns. On the first column a countdown should be displayed and on the second column an additional text, that explains what is the countdown for. Below you see my code that works ... more or less. I have a listview with multiple rows an the timers are ticking. One problem is: the set.Text() in my runnable seems to override all rows. E.g. runnable for row 1 sets text also to row 2 and 3, runnable for row 2 sets text also for 1 and 3 and so on. This has the effect that the first column blinks (with correct values and values of other rows). How can I set text for a specific row in a listview?

Next problem: the runnable is running on and on even if I remove callbacks from handler. But when the activity is in background or closed the timer ticking is not needed and I don't want to waste system resources.

My Activity:

public class TimerActivity extends ListActivity {

MyTimerAdapter myTimerAdapter = null;
ArrayList<Long> timerList = new ArrayList<Long>();
ArrayList<String> textList = new ArrayList<String>();

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.listactivity);

    myTimerAdapter = new MyTimerAdapter(this, R.layout.row, R.id.tv_timer, R.id.tv_text);
    setListAdapter(myTimerAdapter);
}

@Override
protected void onResume() {
    super.onResume();
    refreshView();
}

@Override
protected void onPause() {
    myTimerAdapter.clear();
    myTimerAdapter.notifyDataSetChanged();
    super.onPause();
}

private void refreshView() {

    myTimerAdapter.clear();
    timerList.clear();
    textList.clear();

    // some code to read database and fill 
    // array timerList with a long value (used for displaying a countdown) 
    // and textList with some additional text

    myTimerAdapter.add(timerList, textList);
    myTimerAdapter.notifyDataSetChanged();
}

}

My Adapter:

public class MyTimerAdapter extends ArrayAdapter<String> {
private Activity mContext;
private ArrayList<Long> mTimer;
private ArrayList<String> mText;
private int mViewId;
private int mViewIdFieldTimer;
private int mViewIdFieldText;
private int listSize;
private ArrayList<Handler> handlerList = new ArrayList<Handler>();
private ArrayList<TimerRunnable> runList = new ArrayList<TimerRunnable>();

public MyTimerAdapter(Activity context, int textViewResId, int tv1, int tv2) {
    super(context, textViewResId);
    mContext = context;
    mViewId = textViewResId;
    mViewIdFieldTimer = tv1;
    mViewIdFieldText = tv2;
    listSize = 0;
}

public void add(ArrayList<Long> timer, ArrayList<String> text) {
    mTimer = timer;
    mText = text;
    listSize = mText.size();
    handlerList.clear();
    runList.clear();
}

@Override
public void clear() {
    super.clear();
    int i;
    for (i=0; i<listSize; i++) {
        handlerList.get(i).removeCallbacksAndMessages(runList.get(i));
        runList.get(i).stopHandler();
    }
}

@Override
public int getCount() {
    return listSize;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
        LayoutInflater vi = mContext.getLayoutInflater();
        v = vi.inflate(mViewId, null);
    }

    long timerLine = mTimer.get(position);
    if (timerLine != 0) {
        TextView tvTimer = (TextView) v.findViewById(mViewIdFieldTimer);

        if (tvTimer != null) {
            tvTimer.setTag(position);
            final Handler mTimerHandler = new Handler();
            TimerRunnable timerTask = new TimerRunnable(tvTimer, tvTimer.getTag().toString(), timerLine);
            mTimerHandler.post(timerTask);

            // save in array to stop later
            handlerList.add(mTimerHandler);
            runList.add(timerTask);
        }
    }

    String textLine = mText.get(position);
    if (textLine != null) {
        TextView tvText = (TextView) v.findViewById(mViewIdFieldText);
        if (tvText != null) {
            tvText.setText(textLine);
        }
    }

    return v;
}
}

My Runnable:

public class TimerRunnable implements Runnable {
private TextView tv;
final Handler mTimerHandler = new Handler();
String tag;
long endtime;
long sec;

public TimerRunnable (TextView tv, String tag, long endtime) {
    this.tv = tv;
    this.tag = tag;
    this.endtime = endtime;
}

public void run() {
    if (tv.getTag().toString().equals(tag)) {
        Calendar cal = Calendar.getInstance();
        sec = endtime - (cal.getTimeInMillis() / 1000); //endtime - aktuelle Zeit
        if (sec >= 0) {

            // some code formatting the time in seconds to something like hh:mm:ss (var String txt)

            tv.setText(txt);

            System.out.println(txt);  // only for tests; so I could see that runnable is still running

            mTimerHandler.postDelayed(this, 1000);
        } 
    }
}

public void stopHandler() {
    mTimerHandler.removeCallbacksAndMessages(null);
}
}

解决方案

I would do as follows:

  1. Use a Timer (http://developer.android.com/reference/java/util/Timer.html) and let it run periodically every second or whatever.
  2. Create the Timer object in Activities onCreate() and cancel() the timer in Activities onDestroy() (or even better start it in onResume and cancel it in onPause()). So thats the simplest way how to avoid memory leaks and that your timer is running even if the activity is closed.
  3. update the ListView from the running timer. There are 2 options:

    • Simply call adapter.notifyDatasetChanged() from your timer. But this could lead to a "blinking" listview. However you could try and checkout if this could work in your application, since is the simplest implementation.
    • Update directly the TextViews that are currently visible in the ListView

      private void updateTime(){
      
      int firstVisibleItemIndex = listView.getFirstVisiblePosition();
      
      for (int i = 0; i < listView.getChildCount(); i++) {
          View v = listView.getChildAt(i);
      
              YourItem item = (YourItem)adapter
                      .getItem(firstVisibleItemIndex + i));
      
              ViewHolder vh = (ViewHolder) v.getTag();
                                  // Calculate the time somehow, i.e. call a methot on your data item
                                  vh.tvTimer.setText(item.getElapsedTime());
      
      
          }
      
      }
      

      }

So I guess the second option would be the best. You may wonder what ViewHolder is. ViewHolder is a pattern that increases the performance of your ListView. http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder The problem of your adapter is, that findViewById() will be called everytime the user scrolls. findViewById() is a expensive method call, since it has to traverse all view childs to find the one with the given id. In your simple adapter it may not have to much impact (since you have only a single child view, the TextView). But ViewHolder is something you should use always in your Adapter implementation

这篇关于安卓:在处理程序,并可以运行一个ListView多个定时器。问题2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆