1. 程式人生 > >Recyclerview的一些個人理解與使用(三)列表中的計時器

Recyclerview的一些個人理解與使用(三)列表中的計時器

昨天專案升級了一版後,手頭又有了些許閒餘時間,剛好之前看到群裡的小夥伴由於有個定時器的需求,加入到Recyclerview中遇到了許多麻煩,今天剛好寫一個demo練練手,看看自己能否解決這個問題。
按照一般的列表Recyclerview來做,先是最簡單的介面佈局,與上一篇文章的佈局類似

都是一個簡單的列表佈局,為了便於理解,我們稍微替換一下id已做區分。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.ztn.recyclerviewdemo.timerecyclerview.TimeRecyclerViewActivity">
<RelativeLayout android:id="@+id/time_activity_recyclerview_rl"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView android:id="@+id/time_activity_recyclerview" android:layout_width="match_parent" android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView> </RelativeLayout> </LinearLayout>

item佈局與上個介面相同,這裡就不在贅述。

我們在介面中建立計時器陣列,也建立40個以便於介面滑動,然後找出問題

   List<CountDownTimer> countDownTimers;
   countDownTimers = new ArrayList<>();
   for (int i = 0; i < 40; i++) {
            countDownTimers.add(getCountDownTimer(i));
        }

getCountDownTimer()方法對CountDownTimer進行了初始化,以及設定資料,這裡先寫出我的開始的思想;

 private CountDownTimer getCountDownTimer(final int position) {
//        return null;
        return new CountDownTimer(36000, 10) {
            @Override
            public void onTick(long millisUntilFinished) {
              //獲取item的textview並且設定資料
            }

            @Override
            public void onFinish() {
                //獲取item的textview並且設定資料
            }
        };
    }

將新建好的CountDownTimer傳入adapter中,其中的Activity的為什麼要傳,這裡:

//初始化需要的引數
    public TimeRecyclerViewAdapter(Context context, List<CountDownTimer> listData) {
        super(context, listData);
    }

    /**
     * 建立View
     *
     * @param parent
     * @return
     */
    @Override
    protected TimeRecyclerViewHolder onCreateItemViewHolder(ViewGroup parent) {
        return new TimeRecyclerViewHolder(inflater.inflate(R.layout.a_simple_text_view, parent, false));
    }

    public void setOnClickItemListener(OnClickItemListener onClickItemListener) {
        this.onClickItemListener = onClickItemListener;
    }


    /**
     * 給View設定資料
     *
     * @param timeRecyclerViewHolder
     * @param position
     */
    @Override
    protected void onBindItemViewHolder(TimeRecyclerViewHolder timeRecyclerViewHolder, int position) {
        timeRecyclerViewHolder.initView(context, listData.get(position), onClickItemListener, position
        ,timeRecyclerViewActivity);
    }

    /**
     * 便於實現點選事件
     */

    public interface OnClickItemListener {
        void onClick(CountDownTimer countDownTimer, int position);
    }

在holder中使用暴露對textview的資料設定,在CountDownTimer初始化時對textview進行資料設定:

package com.ztn.recyclerviewdemo.timerecyclerview.timeadapter;

import android.content.Context;
import android.os.CountDownTimer;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;

import com.ztn.recyclerviewdemo.R;
import com.ztn.recyclerviewdemo.timerecyclerview.TimeRecyclerViewActivity;

import java.util.HashMap;
import java.util.List;


/**
 * Created by ztn on 2017/3/28
 */

public class TimeRecyclerViewHolder extends RecyclerView.ViewHolder {
    TextView textView;
    CountDownTimer countDownTimer;
    TimeRecyclerViewHolder timeRecyclerViewHolder;

    public TimeRecyclerViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.simple_tv);
        timeRecyclerViewHolder = this;
    }

    public void initView(Context context, final CountDownTimer countDownTimer,
                         final TimeRecyclerViewAdapter.OnClickItemListener onClickItemListener,
                         final int position) {
                 textView.setText("我是第"+position+“個計時器”)        
    }

    public void setTextViewText(String text) {
        textView.setText(text);
    }

基本就完成了,執行一下看看效果如何
這裡寫圖片描述

點選後計時器確實運行了,但是滑動後超過介面的Recyclerview的item滑回來後有初始化成了“我是第XX個計時器的格式”,不用說,肯定是Recyclerview的回收機制造成的,由於上滑出見面的Recyclerview的item被回收了,所以在滑回來後對他初始就是初始化咱們holder中的東西。那麼怎麼個item一個正常的初始化那,通常是以加標籤的形式來確定的,看看我們的計時器現在有那幾種可能那:
1.最開始的狀態,當然就是”我是第XX個計時器”的格式
2.就是我們點選後的狀態了“XXXXXX”這麼個格式的數字計時了
3.就是我們計時結束後的一句話了這裡我給出的是“倒計時結束了”

那麼,現在我們來看一看一份實現計時器的完成的程式碼,先是Activity:

**
 * Created by ztn on 2017/4/5
 */
public class TimeRecyclerViewActivity extends BaseActivity {
    TimeRecyclerViewActivityHolder timeRecyclerViewActivityHolder;
    TimeRecyclerViewAdapter timeRecyclerViewAdapter;
    List<CountDownTimer> countDownTimers;

    public List<Boolean> IsTimeStart;//用來確定是否已經開始計算時間,即是否進行了點選
    public List<Boolean> IsTimeGone;//用來確定是否已經結束計算時間,即點選後時間是否已經結束
    TimeRecyclerViewActivity timeRecyclerViewActivity;

    public static Intent newIntent(Context context) {
        return new Intent(context, TimeRecyclerViewActivity.class);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_time);
        timeRecyclerViewActivityHolder = new TimeRecyclerViewActivityHolder(this);
        timeRecyclerViewActivity = this;
        countDownTimers = new ArrayList<>();
        IsTimeStart = new ArrayList<>();
        IsTimeGone = new ArrayList<>();
        for (int i = 0; i < 40; i++) {
            IsTimeStart.add(false);
            IsTimeGone.add(false);
            countDownTimers.add(getCountDownTimer(i));
        }
        timeRecyclerViewAdapter = new TimeRecyclerViewAdapter(getContext(), countDownTimers, timeRecyclerViewActivity);
        timeRecyclerViewActivityHolder.recyclerView.setAdapter(timeRecyclerViewAdapter);
        timeRecyclerViewAdapter.notifyDataSetChanged();
        timeRecyclerViewAdapter.setOnClickItemListener(new TimeRecyclerViewAdapter.OnClickItemListener() {
            @Override
            public void onClick(CountDownTimer countDownTimer, int position) {
                IsTimeStart.set(position, true);
                countDownTimer.start();
                Toast.makeText(getContext(), "第" + position + "個countDownTimer開始了", Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 通過viewholder的標籤獲得對應的viewholder
     * @param position
     * @return
     */
    private TimeRecyclerViewHolder getState(int position) {
        return (TimeRecyclerViewHolder) timeRecyclerViewActivityHolder.recyclerView.
                getChildViewHolder(timeRecyclerViewActivityHolder.recyclerView.findViewWithTag(position));
    }


    private CountDownTimer getCountDownTimer(final int position) {
//        return null;
        return new CountDownTimer(36000, 10) {
            @Override
            public void onTick(long millisUntilFinished) {
                try {
                    IsTimeGone.set(position, false);
                    TimeRecyclerViewHolder timeRecyclerViewHolder = getState(position);
                    timeRecyclerViewHolder.setTextViewText(String.valueOf(millisUntilFinished));
                } catch (NullPointerException ignore) {
                }
            }

            @Override
            public void onFinish() {
                try {
                    IsTimeGone.set(position, true);
                    TimeRecyclerViewHolder timeRecyclerViewHolder = getState(position);
                    timeRecyclerViewHolder.setTextViewText("倒計時結束了");
                } catch (NullPointerException ignore) {
                }
            }
        };
    }
}

我們在Activity中建立了兩個陣列以標記對應item的狀態,傳入adapter中便於viewholder得到自己的對應狀態。

再來就是我們的完整的adapter:


/**
 * Created by ztn on 2017/3/28
 */

public class TimeRecyclerViewAdapter extends SimpleRecycleViewAdapter<CountDownTimer, TimeRecyclerViewHolder> {
    private OnClickItemListener onClickItemListener;
    private TimeRecyclerViewActivity timeRecyclerViewActivity;//獲取Activity中的標籤

    public TimeRecyclerViewAdapter(Context context, List<CountDownTimer> listData,
                                   TimeRecyclerViewActivity timeRecyclerViewActivity) {
        super(context, listData);
        this.timeRecyclerViewActivity = timeRecyclerViewActivity;
    }

    /**
     * 建立View
     *
     * @param parent
     * @return
     */
    @Override
    protected TimeRecyclerViewHolder onCreateItemViewHolder(ViewGroup parent) {
        return new TimeRecyclerViewHolder(inflater.inflate(R.layout.a_simple_text_view, parent, false));
    }

    public void setOnClickItemListener(OnClickItemListener onClickItemListener) {
        this.onClickItemListener = onClickItemListener;
    }

    /**
     * 重寫了onBindViewHolder,對viewholder設定了標籤
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //設定標籤
        holder.itemView.setTag(position);
//        holder.setIsRecyclable(false);
        super.onBindViewHolder(holder, position);
    }

    /**
     * 給View設定資料
     *
     * @param timeRecyclerViewHolder
     * @param position
     */
    @Override
    protected void onBindItemViewHolder(TimeRecyclerViewHolder timeRecyclerViewHolder, int position) {
        timeRecyclerViewHolder.initView(context, listData.get(position), onClickItemListener, position
        ,timeRecyclerViewActivity);
    }

    /**
     * 便於實現點選事件
     */

    public interface OnClickItemListener {
        void onClick(CountDownTimer countDownTimer, int position);
    }
}

通過重寫了onBindViewHolder,對viewholder設定了標籤,這樣便於我們在Activity中取到對應的標籤

再來是我們的viewholder

/**
 * Created by ztn on 2017/3/28
 */

public class TimeRecyclerViewHolder extends RecyclerView.ViewHolder {
    TextView textView;
    CountDownTimer countDownTimer;
    TimeRecyclerViewHolder timeRecyclerViewHolder;

    public TimeRecyclerViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.simple_tv);
        timeRecyclerViewHolder = this;
    }

    public void initView(Context context, final CountDownTimer countDownTimer,
                         final TimeRecyclerViewAdapter.OnClickItemListener onClickItemListener,
                         final int position, TimeRecyclerViewActivity timeRecyclerViewActivity) {
        this.countDownTimer = countDownTimer;
        if (timeRecyclerViewActivity.IsTimeStart.get(position)) {
            if (timeRecyclerViewActivity.IsTimeGone.get(position)) {
                textView.setText("倒計時結束了");
            }
        } else {
            textView.setText("我是第" + position + "個計時器");
        }
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onClickItemListener != null)
                    onClickItemListener.onClick(countDownTimer, position);
            }
        });
    }

    public void setTextViewText(String text) {
        textView.setText(text);
    }
}

在viewholder中我們在對於textview的初始化的時候對於它的狀態進行了判斷,當然你可加上一個陣列在記錄計時器中的數字,直接在初始化的時候給item,我這裡是通過計時器0.01秒對textview進行一次繪製,讓使用者無感知,否則可能會由於時間間隔過長導致開始滑上去開始是會是空白。

我們在試一遍,點選item,計時開始了,滑下去,滑回來,恩,textview顯示了正確的狀態,效果完成。

我的這種方法只是眾多方法的一種,還有很多別的方法,比如只建立單個計時器,用單個計時器對所有的item進行處理,這樣應該能減少一些記憶體消耗,大家可以自己加以嘗試。
專案地址:

如有疑問大家可以新增 121606151 這個qq群@Crazy(即本人)即可,歡迎大家前來交流。