轉載位址:https://ruzhan123.github.io/2016/07/01/2016-07-01-01-%E5%AF%B9RecyclerViewItem%E5%81%9A%E5%8A%A8%E7%94%BB/#rd
對RecyclerView Item做動畫,剛剛開始研究的時候一些坑
在這裡把一些設計思路分享出去
添加動态位移,靜态位移,縮放等動畫,保證了動畫狀态的平滑銜接
對RecyclerView Item做動畫 RecyclerView,ListView這些具有Item複用性的View,想要對其Item做動畫
需要注意以下幾點:
1,如果要一點選,讓所有Item做動畫的效果。例如,上圖的編輯和取消,這樣的動态動畫。可以對所有ViewHolder中的View直接做動畫。但是需要在onBindViewHolder方法中對複用的item做靜态動畫,保證動畫狀态的平滑銜接。
2,每一個Item的特有屬性,例如,上圖checkbox的選中狀态,都需要把狀态字段放到對應的Java bean中, 并在onBindViewHolder方法從java bean取出狀态值,設定到view裡。
首先,對一些細節進行分析:
如何設計一個自定義View,來讓他可以自己移動,做動畫起來?
1,首先,建立一個View,他是RecyclerView Item的根布局:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| public class SlideRelativeLayout extends RelativeLayout {
public static final String TAG = SlideRelativeLayout.class.getSimpleName();
private CheckBox mCheckBox;
private RelativeLayout mContentSlide;
private int mOffset;
public SlideRelativeLayout(Context context) {
super(context);
}
public SlideRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mCheckBox = (CheckBox) findViewById(R.id.item_checkbox);
mContentSlide = (RelativeLayout) findViewById(R.id.item_content_rl);
setOffset(35);
}
public void setOffset(int offset) {
mOffset = (int) (getContext().getResources().getDisplayMetrics().density * offset + 0.5f);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void openAnimation() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setIntValues(0, 1);
valueAnimator.setDuration(300);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
int endX = (int) (-mOffset * fraction);
doAnimationSet(endX, fraction);
}
});
valueAnimator.start();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void closeAnimation() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setIntValues(0, 1);
valueAnimator.setDuration(150);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
int endX = (int) (-mOffset * (1 - fraction));
doAnimationSet(endX, (1 - fraction));
}
});
valueAnimator.start();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void doAnimationSet(int dx, float fraction) {
mContentSlide.scrollTo(dx, 0);
mCheckBox.setScaleX(fraction);
mCheckBox.setScaleY(fraction);
mCheckBox.setAlpha(fraction * 255);
}
public void open() {
mContentSlide.scrollTo(-mOffset, 0);
}
public void close() {
mContentSlide.scrollTo(0, 0);
}
}
|
這裡,在View樹建立完畢之後找到我們需要做動畫的子View:
1
2
3
4
5
6
7
8
| @Override
protected void onFinishInflate() {
super.onFinishInflate();
mCheckBox = (CheckBox) findViewById(R.id.item_checkbox);
mContentSlide = (RelativeLayout) findViewById(R.id.item_content_rl);
setOffset(35);
}
|
然後,設計4個方法,分别為:動态的打開動畫,動态的關閉動畫,靜态的打開動畫,靜态的關閉動畫。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void openAnimation() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setIntValues(0, 1);
valueAnimator.setDuration(300);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
int endX = (int) (-mOffset * fraction);
doAnimationSet(endX, fraction);
}
});
valueAnimator.start();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void closeAnimation() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setIntValues(0, 1);
valueAnimator.setDuration(150);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
int endX = (int) (-mOffset * (1 - fraction));
doAnimationSet(endX, (1 - fraction));
}
});
valueAnimator.start();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void doAnimationSet(int dx, float fraction) {
mContentSlide.scrollTo(dx, 0);
mCheckBox.setScaleX(fraction);
mCheckBox.setScaleY(fraction);
mCheckBox.setAlpha(fraction * 255);
}
public void open() {
mContentSlide.scrollTo(-mOffset, 0);
}
public void close() {
mContentSlide.scrollTo(0, 0);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void doAnimationSet(int dx, float fraction) {
mContentSlide.scrollTo(dx, 0);
mCheckBox.setScaleX(fraction);
mCheckBox.setScaleY(fraction);
mCheckBox.setAlpha(fraction * 255);
}
|
對子View做動畫我采取的政策是:使用屬性動畫,在每一貞動畫裡擷取到對應的值,對子View做相應的動畫,例如:動态的打開動畫。
onAnimationUpdate方法,顯示每一貞動畫都會回調一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void openAnimation() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setIntValues(0, 1);
valueAnimator.setDuration(300);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
int endX = (int) (-mOffset * fraction);
doAnimationSet(endX, fraction);
}
});
valueAnimator.start();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void doAnimationSet(int dx, float fraction) {
mContentSlide.scrollTo(dx, 0);
mCheckBox.setScaleX(fraction);
mCheckBox.setScaleY(fraction);
mCheckBox.setAlpha(fraction * 255);
}
|
這樣RecylerView 帶有動态動畫和靜态動畫的View就設計好了。
2,在bind方法中使用靜态動畫,動态動畫對外提供方法調用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| private class SlideViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private SlideRelativeLayout mSlideRelativeLayout;
private CheckBox mCheckBox;
private ItemBean mItemBean;
public SlideViewHolder(View itemView) {
super(itemView);
mSlideRelativeLayout = (SlideRelativeLayout) itemView.findViewById(R.id.item_root);
mCheckBox = (CheckBox) itemView.findViewById(R.id.item_checkbox);
itemView.setOnClickListener(this);
}
public void bind(ItemBean itemBean) {
mItemBean = itemBean;
mCheckBox.setChecked(itemBean.isChecked());
switch (mState) {
case NORMAL:
mSlideRelativeLayout.close();
break;
case SLIDE:
mSlideRelativeLayout.open();
break;
}
}
public void openItemAnimation() {
mSlideRelativeLayout.openAnimation();
}
public void closeItemAnimation() {
mSlideRelativeLayout.closeAnimation();
}
|
可以看到靜态動畫在bind裡調用,打開或者關閉是由mState變量決定的。而動态的滑動需要手動調用,那怎麼來使用這些動畫呢?
動态動畫的使用方法:存儲所有建立出來的ViewHolder,統一調用動态動畫方法。并設定mState變量值,防止滑動時動畫不能平滑銜接.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| private List<SlideViewHolder> mSlideViewHolders = new ArrayList<>();
public void openItemAnimation() {
mState = SLIDE;//
for (SlideViewHolder holder : mSlideViewHolders) {
holder.openItemAnimation();
}
}
public void closeItemAnimation() {
mState = NORMAL;
for (SlideViewHolder holder : mSlideViewHolders) {
holder.closeItemAnimation();
}
}
|
而外面又是這樣調用的:
1
2
3
4
5
6
7
8
9
10
| private void editItems() {
if ("編輯".equals(mRightTV.getText().toString())) {
mRightTV.setText("取消");
mSlideAdapter.openItemAnimation();
} else if ("取消".equals(mRightTV.getText().toString())) {
mRightTV.setText("編輯");
mSlideAdapter.closeItemAnimation();
}
}
|
總體就是:點選按鈕 – 變量ViewHolder集合做動态動畫,并設定mState變量 – 手機滑動螢幕走bind方法又是根據mState做靜态動畫
動畫進而平滑的銜接起來
再來看一次效果圖:
對RecyclerView Item做動畫 動态動畫起先,設定狀态值,引導處理正确的靜态動畫,RecyclerView item的動畫處理是不是變簡單了。
還有,對item的特殊資料需要在對應的java bean裡設定值,在bind方法取值設定到item中去。
分析就到這裡了,小程式一枚。
Demo下載下傳:我的Github