简体   繁体   English

在RecyclerViewAdapter中实现计时器

[英]Implementing timers in RecyclerViewAdapter

I have problem with implementing countdown timers in RecyclerView. 我在RecyclerView中实现倒数计时器时遇到问题。 Data input to RecyclerView comes from Dialog, where user types name of the item, sets with SeekBar time in seconds and chooses a date with custom widget. 输入到RecyclerView的数据来自Dialog,用户可以在其中键入项目名称,设置SeekBar时间(以秒为单位)并选择带有自定义小部件的日期。 Information are saved in Realm database. 信息保存在Realm数据库中。

Problem is that after restarting app or adding a new item, all timers in all items restarts and start to count from the beginning. 问题是重新启动应用程序或添加新项目后,所有项目中的所有计时器都会重新启动并从头开始计数。 How to implement timers that would count countinously even when app won't be active or during adding a new item. 如何实现即使在应用程序不处于活动状态或添加新项目时也要计数的计时器。 Thank you in advance for help. 预先感谢您的帮助。

public class AdapterDrops extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SwipeListener {

public static final int COUNT_FOOTER = 1;
public static final int COUNT_NO_ITEMS = 1;
public static final int ITEM = 0;
public static final int NO_ITEM = 1;
public static final int FOOTER = 2;
private final ResetListener mResetListener;
private MarkListener mMarkListener;
//inflater object which converts xml file to view object
private LayoutInflater mInflater;
public RealmResults<Drop> mResults;
private AddListener mAddListener;
private int mFilterOption;
private Realm mRealm;
private Context mContext;
Handler handler;
public long duration;


public AdapterDrops(Context context, Realm realm, RealmResults<Drop> results, AddListener listener, MarkListener markListener, ResetListener resetListener) {
    mContext = context;
    mInflater = LayoutInflater.from(context);
    update(results);
    mRealm = realm;
    mAddListener = listener;
    mMarkListener = markListener;
    mResetListener = resetListener;

}

public void update(RealmResults<Drop> results) {
    mResults = results;
    mFilterOption = AppBucketDrops.load(mContext);
    //notification do Apdapter that database was changed
    notifyDataSetChanged();

}

@Override
public long getItemId(int position) {
    if (position < mResults.size()) {
        return mResults.get(position).getAdded();
    }
    return RecyclerView.NO_ID;
}

@Override
public int getItemViewType(int position) {
    if (!mResults.isEmpty()) {
        if (position < mResults.size()) {
            return ITEM;
        } else {
            return FOOTER;
        }
    } else {
        if (mFilterOption == Filter.COMPLETE ||
                mFilterOption == Filter.INCOMPLETE) {
            if (position == 0) {
                return NO_ITEM;
            } else {
                return FOOTER;
            }
        } else {
            return ITEM;
        }
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == FOOTER) {
        View view = mInflater.inflate(R.layout.footer, parent, false);
        //footerHolder class which we created below
        return new FooterHolder(view);
    } else if (viewType == NO_ITEM) {
        View view = mInflater.inflate(R.layout.no_item, parent, false);
        return new NoItemsHolder(view);
    } else {
        //layourInflater converts xml file to java View object
        View view = mInflater.inflate(R.layout.row_drop, parent, false);
        return new DropHolder(view, mMarkListener);
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof DropHolder) {
        DropHolder dropHolder = (DropHolder) holder;
        //returning an item from the paricular position
        Drop drop = mResults.get(position);
        //seting MtextView to proper drop.getWhat text
        dropHolder.setWhat(drop.getWhat());
        dropHolder.setWhen(drop.getWhen());
        dropHolder.setTimer(drop.getTimer());
        dropHolder.setBackground(drop.isCompleted());
    }

}

@Override
public int getItemCount() {
    if (!mResults.isEmpty()) {
        return mResults.size() + COUNT_FOOTER;
    } else {
        if (mFilterOption == Filter.LEAST_TIME_LEFT
                || mFilterOption == Filter.MOST_TIME_LEFT
                || mFilterOption == Filter.NONE) {
            return 0;
        } else {
            return COUNT_NO_ITEMS + COUNT_FOOTER;
        }
    }

}


@Override
public void onSwipe(int position) {
    //delete item with transaction from database
    if (position < mResults.size()) {
        mRealm.beginTransaction();
        mResults.get(position).deleteFromRealm();
        mRealm.commitTransaction();
        notifyItemRemoved(position);
    }
    resetFilterIfEmpty();
}

private void resetFilterIfEmpty() {
    if (mResults.isEmpty() && (mFilterOption == Filter.COMPLETE || mFilterOption == Filter.INCOMPLETE)) {
        mResetListener.onReset();
    }
}

public void markComplete(int position) {
    //checking that item is not a footer
    if (position < mResults.size()) {
        mRealm.beginTransaction();
        mResults.get(position).setCompleted(true);
        mRealm.commitTransaction();
        notifyItemChanged(position);
    }
}

public static class DropHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    TextView mTextWhat;
    TextView mTextWhen;
    MarkListener mMarkListener;
    Context mContext;
    View mItemView;
    TextView mTimer;
    //timer
    Handler handler;
    public long timeRemaining;
    public Drop drop;

    public DropHolder(View itemView, MarkListener listener) {
        super(itemView);
        mItemView = itemView;
        mContext = itemView.getContext();
        itemView.setOnClickListener(this);
        mTextWhat = (TextView) itemView.findViewById(R.id.tv_what);
        mTextWhen = (TextView) itemView.findViewById(R.id.tv_when);
        mTimer = (TextView) itemView.findViewById(R.id.tv_timer);
        mMarkListener = listener;

    }

    public void setWhat(String what) {
        mTextWhat.setText(what);
    }

    public Drop getDrop(Drop drop) {
        return drop;
    }

    public void setTimer(long timer) {

        handler = new Handler();
        timeRemaining = timer * 1000;
        final Runnable runnable = new Runnable() {
            @Override
            public void run() {
                timeRemaining = timeRemaining - 1000;

                if (timeRemaining > 0) {
                    handler.postDelayed(this, 1000);
                    timeRemaining = timeRemaining / 1000;
                    mTimer.setText(Long.toString(timeRemaining));
                    timeRemaining = timeRemaining * 1000;
                }
                if (timeRemaining == 0) {
                    mTimer.setText("Czas na lek !");
                }
            }
        };
        //kickstart
        handler.postDelayed(runnable, 1000);
    }

    @Override
    public void onClick(View v) {
        mMarkListener.onMark(getAdapterPosition());
    }

    public void setBackground(boolean completed) {
        Drawable drawable;
        if (completed) {
            drawable = ContextCompat.getDrawable(mContext, R.color.colorLightBlueAfterClick);
        } else {
            drawable = ContextCompat.getDrawable(mContext, R.drawable.bg_row_drop);
        }
        /*if(Build.VERSION.SDK_INT > 15){
            mItemView.setBackground(drawable);
        } else{
            mItemView.setBackgroundDrawable(drawable);
        }*/
        Util.setBackground(mItemView, drawable);

    }

    public void setWhen(long when) {
        mTextWhen.setText(DateUtils.getRelativeTimeSpanString(when, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS, 0));

    }
}

public static class NoItemsHolder extends RecyclerView.ViewHolder {

    public NoItemsHolder(View itemView) {
        super(itemView);
    }
}

public class FooterHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
    Button mBtnAdd;

    public FooterHolder(View itemView) {
        super(itemView);
        mBtnAdd = (Button) itemView.findViewById(R.id.btn_footer);
        mBtnAdd.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        //were using from this place AddListener
        mAddListener.add();
    }
}

} }

Instead of saving the countdown time, save the end datetime (datetime now + countdown) and base the logic on that. 不用保存倒计时时间,而是保存结束日期时间(现在的日期时间+倒数),并在此基础上进行逻辑运算。 That way when the app resets or views reset, the countdown logic will continue where it left off by comparing the datetime now with the end datetime. 这样,当应用程序重置或视图重置时,通过将现在的日期时间与结束日期时间进行比较,倒数计时逻辑将从上次停止的地方继续。

Furthermore, the code you posted is posting a Runnable every second for every view, even for the ones that are not visible to the user. 此外,您发布的代码正在为每个视图每秒发布一个Runnable ,即使对于用户不可见的视图也是如此。 This causes poor performance of the app. 这会导致应用程序性能下降。 Instead, create a single timer, possible controlled by the Activity hosting the RecyclerView.Adapter , and it'll call RecyclerView.Adapter.notifyDataSetChanged() which will in turn call RecyclerView.Adapter.onBindViewHolder() to update the UI. 而是创建一个计时器,该计时器可能由托管RecyclerView.AdapterActivity进行控制,它将调用RecyclerView.Adapter.notifyDataSetChanged() ,后者将依次调用RecyclerView.Adapter.onBindViewHolder()来更新UI。 Hence the adapter will update all visible UI elements which means the countdown UIs (only the visible ones of course) will be updated every second too. 因此,适配器将更新所有可见的UI元素,这意味着倒数UI(当然也只有可见的UI)也将每秒更新一次。 Even when scrolling RecyclerView.Adapter.onBindViewHolder() will be called so that taken care of. 即使在滚动时,也会调用RecyclerView.Adapter.onBindViewHolder()来处理。 This will greatly improve the performance of app when the data-set is large. 当数据集很大时,这将大大提高应用程序的性能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM