私は現在、そのコア機能で、リサイクラービューで複数のカウントダウンタイマーを処理するアプリを開発中です。recyclerviewのカウントダウンタイマーを処理する - android
ここで実装した方法は、作成されたすべてのViewHolderに対して単にカウントダウンタイマーを開始することです。私の問題は時々、タイマーが終了するたびに、他のすべてのタイマーは変な動作をしています。
たとえば、タイマーが起動するたびに、そのタイマーのViewHolderは、上記のクラスが参照しているCardView UIコンポーネントの背景を変更します。
実際には、1つのタイマーが稼動している場合、他のビューホルダーにアクセスして変更しています。
ここに私のアダプタコードがあります。私は可能な限り文書化しようとしました:
package bikurim.silverfix.com.bikurim.adapters;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.CountDownTimer;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import bikurim.silverfix.com.bikurim.Constants;
import bikurim.silverfix.com.bikurim.models.Family;
import bikurim.silverfix.com.bikurim.R;
import bikurim.silverfix.com.bikurim.utils.CountDownManager;
import bikurim.silverfix.com.bikurim.utils.TimerEventListener;
public class FamiliesAdapter extends RecyclerView.Adapter<FamiliesAdapter.FamilyViewHolder> implements Filterable, TimerEventListener {
// last position holds the last position of the element that was added, for animation purposes
private static int lastPosition = -1;
private Context context;
private ArrayList<Family> families;
private ArrayList<Family> dataSet;
private FamilyFilter filter;
private CountDownManager countDownManager;
public FamiliesAdapter(Context context, ArrayList<Family> families) {
this.context = context;
this.dataSet = families;
this.families = dataSet;
countDownManager = new CountDownManager(Constants.Values.TIME_INTERVAL, this);
countDownManager.start();
}
@Override
public FamilyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Inflating the view from a given layout resource file
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.family_list_item, parent, false);
final FamilyViewHolder pvh = new FamilyViewHolder(v);
return pvh;
}
@Override
public void onBindViewHolder(final FamilyViewHolder holder, int position) {
// Binds the Family object to the holder view
Family family = families.get(position);
holder.bindData(family);
countDownManager.addTimer(holder);
// Sets animation on the given view, in case it wasn't displayed before
setSlideAnimation(holder.cardView, position, false);
}
@Override
public int getItemCount() {
return families != null ? families.size() : 0;
}
/* Gets the current the filter object */
@Override
public Filter getFilter() {
if (filter == null)
filter = new FamilyFilter();
return filter;
}
/* The following 2 methods are an implementations of the ViewHolderListener.
* Inside every view holder lies an instance of ViewHolderListener, for communication between the two*/
public void startAnimationOnItem(FamilyViewHolder holder, boolean isEndAnimation) {
setSlideAnimation(holder.cardView, holder.getAdapterPosition(), isEndAnimation);
}
/* Changes the UI of a holder to a time's up view with a flickering ImageButton and a TextView*/
@Override
public void onFinish(FamilyViewHolder holder) {
// Switches between the clock icon to the alarm icon
holder.clock.setVisibility(View.GONE);
holder.remove.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.RIGHT_OF, holder.remove.getId());
params.addRule(RelativeLayout.CENTER_VERTICAL);
holder.timeLeft.setLayoutParams(params);
holder.timeLeft.setText(context.getString(R.string.time_up_message));
setFadeAnimation(holder.remove);
holder.cardView.setBackgroundResource(R.color.cardview_light_background);
startAnimationOnItem(holder, true);
holder.family.timeLeft = 0;
}
@Override
public void onLessThanMinute(FamilyViewHolder holder) {
holder.cardView.setBackgroundResource(R.color.time_up_bg);
holder.isBackgroundChanged = true;
}
/* Starts a slide in animation for a given Card View */
private void setSlideAnimation(View viewToAnimate, int position, boolean isEndAnimation) {
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
if (!isEndAnimation) {
if (position > lastPosition) {
viewToAnimate.startAnimation(animation);
lastPosition = position;
return;
}
}
viewToAnimate.startAnimation(animation);
}
private void setFadeAnimation(View viewToAnimate) {
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(viewToAnimate, "alpha", 1f, .1f);
fadeOut.setDuration(750);
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(viewToAnimate, "alpha", .1f, 1f);
fadeIn.setDuration(750);
final AnimatorSet mAnimationSet = new AnimatorSet();
mAnimationSet.play(fadeIn).after(fadeOut);
mAnimationSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mAnimationSet.start();
}
});
mAnimationSet.start();
}
public void removeData(int pos, FamilyViewHolder holder) {
// Sets the last position to the given deleted position for animation purposes
lastPosition = pos;
// Removes the family object from the data set
families.remove(pos);
notifyItemRemoved(pos);
notifyItemRangeChanged(pos, getItemCount());
// Cancels the the timer and removes it from the entry set
countDownManager.cancelTimer(holder);
}
/* Cancels the timers and clears the entry set */
public void cancelTimers() {
countDownManager.reset();
countDownManager.stop();
}
/* Clears the adapter's data and resets the last position to -1 */
public void clearData() {
cancelTimers();
filter = null;
lastPosition = -1;
}
/* The official view holder of the adapter. Holds references to the relevant views inside the layout, and */
public static class FamilyViewHolder extends RecyclerView.ViewHolder {
public boolean isBackgroundChanged;
public CardView cardView;
public TextView personLname;
public TextView timeLeft;
public TextView visitors;
public ImageView clock;
public ImageButton remove;
public Family family;
private ColorStateList colorStateList;
public FamilyViewHolder(View itemView) {
super(itemView);
// isBackgroundChanged represents whether the holder is under 60 seconds or not
isBackgroundChanged = false;
// Getting the references for the UI components
cardView = (CardView) itemView.findViewById(R.id.cv);
personLname = (TextView) itemView.findViewById(R.id.person_Lname);
visitors = (TextView) itemView.findViewById(R.id.visitors);
timeLeft = (TextView) itemView.findViewById(R.id.person_timeLeft);
clock = (ImageView) itemView.findViewById(R.id.clock);
remove = (ImageButton) itemView.findViewById(R.id.time_up);
// Sets a reference to the old colors of the text view
colorStateList = timeLeft.getTextColors();
}
public void bindData(Family item) {
family = item;
personLname.setText(family.lastName);
visitors.setText("מבקרים: " + family.visitorsNum);
}
public ColorStateList getOriginalColors() {
return colorStateList;
}
}
/* Filter class that queries the constraint on the data set every whenInMillis the user types a key */
private class FamilyFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint.length() == 0 || constraint == null) {
results.values = dataSet;
results.count = dataSet.size();
} else {
ArrayList<Family> queryResults = new ArrayList<Family>();
for (Family f : dataSet) {
if (constraint.charAt(0) == f.lastName.toUpperCase().indexOf(0)) {
queryResults.add(f);
} else if (f.lastName.toUpperCase().contains(constraint.toString().toUpperCase())) {
queryResults.add(f);
}
}
results.values = queryResults;
results.count = queryResults.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
// Now we have to inform the adapter about the new list filtered
synchronized (families) {
families = (ArrayList<Family>) results.values;
}
notifyDataSetChanged();
}
}
}
タイマーの終了時にビューホルダーがスクランブルされるのはなぜですか?
更新:これを少しずつ実装しようとしました。複数のカウントダウンを使用する代わりに、私は更新する必要があるホルダーの配列を保持するものだけを使用します。しかし、問題は同じままです。私のCountDownManagerクラスは次のとおりです:
私は何か間違っていますが、私は問題を特定できないようです。
お返事ありがとうございます、アリ!しかし、どのタイマーがお互いに違うのか、あなたが言うことをどのように実装できますか?私のためにこれを明確にしてください。 –
彼らは皆、それを上げたり下ろしたりしていませんか?カウントアップまたはカウントダウンタイマーは、すべてのビューを循環させてテキストを更新するよう指示することのみを目的としています。 – Ali
私が言ったようにCountDownManagerを実装しようとしました。私は自分のアダプタのコードを更新し、CountDownManagerの新しいコードを投稿に追加しました。それを見ていただけますか?私はまだ同じ問題を抱えている - ViewHoldersは他のViewHoldersからのデータでスクランブルを受ける –