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(即本人)即可,歡迎大家前來交流。