项目中使用ExpandableList,要求分组展开时显示一个动画,
问题:
动画效果位于GroupView上,适配器中getGroupView方法通过判断当前项是点击的分组,
然后进行展开和收起的动画。
原实现方法:
@Override
public View getGroupView(int groupPosition, final boolean isExpanded,
View convertView, ViewGroup parent) {
final ViewHolderGroup holder;
{省略}
if (CommonDetailActivity.onClickPosition == groupPosition) {
if(isExpanded) {
ObjectAnimator animation = ObjectAnimator.ofFloat(holder.right, "rotation", 0, 90);
animation.setDuration(250);
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
CommonDetailActivity.onClickPosition = -1;
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animation.start();
} else {
ObjectAnimator animation = ObjectAnimator.ofFloat(holder.right, "rotation", 90, 0);
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
CommonDetailActivity.onClickPosition = -1;
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animation.setDuration(250);
animation.start();
}
}
return convertView;
}
此种实现方法存在的问题,当展开播放动画时,发现经常出现两个分组都在播放
动画的效果。例如点击一个分组展开,当前分组和其他一个分组箭头的动画
同时播放的现象。
发生原因:
这是因为安卓Adapter的视图缓存复用机制引起的。例如,点击第二个分组展开,
通过日志发现,
生成分组视图时,getGroupView会执行9次,分组视图GV1,GV2,GV3对应的数据项
是随机分配的,例如第一轮时可能GV1对应的position=0,因此GV1播放动画;
第二轮时GV3对应position=0,GV3播放动画。因此,这种缓存视图复用的情况,
可能导致两个分组视图同时响应的情况。其实是初始化过程中视图复用,
对应的数据项变化引起的。
解决方法和思路:
既然初始化过程缓存视图是动态变化的,因此初始化getGroupView过程中,
不进行动画播放处理。初始化完成后,视图对应的数据项稳定时,
再进行动画播放处理。
1)Adapter部分:
记录每个分组数据对应的View的状态,通过groupInfo保存,不需要记录中间视图缓存
复用的过程;初始化过程中不播放动画,直接初始化。根据数据直接显示结果,
例如,展开的分组,箭头角度直接设置为90度,其他的箭头角度为0。
public class CommonDetailAdapter extends BaseExpandableListAdapter {
private static final int STATE_IDLE = 0;
private static final int STATE_EXPANDING = 1;
private static final int STATE_COLLAPSING = 2;
private static final int STATE_EXPANDED = 3;
/** 展开时旋转角度 */
private static final int EXPAND_ROTATE_ANGLE = 90;
/**
* Group控件的信息
* 说明:因为分组视图存在复用的情况因此以最后的视图为准
*/
private Map<Integer, ViewHolderGroup> groupInfo = new HashMap<Integer,
ViewHolderGroup>();
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View getGroupView(int groupPosition, final boolean isExpanded,
View convertView, ViewGroup parent) {
ViewHolderGroup holder = null;
if (convertView == null) {
holder = new ViewHolderGroup();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_common_layout, null);
holder.title = (TextView) convertView.findViewById(R.id.common_item_title);
holder.image = (SmartImageView) convertView.findViewById(R.id.common_item_image);
holder.right = (TextView) convertView.findViewById(R.id.common_item_right);
holder.status = (TextView) convertView.findViewById(R.id.common_item_status);
holder.right.setTypeface(StringUtils.getTypeface());
holder.right.setText(Html.fromHtml(""));
convertView.setTag(holder);
} else {
holder = (ViewHolderGroup) convertView.getTag();
}
holder.isExpanded = isExpanded;
groupInfo.put(groupPosition, holder);
try {
int isFinish = 0;
int total = beans.get(groupPosition).getPic_cate_list().size();
for (int i = 0; i < total; i++) {
if (beans.get(groupPosition).getPic_cate_list().get(i).getIs_finish() == 1) {
isFinish++;
}
}
holder.title.setTypeface(StringUtils.getTypeface());
holder.title.setText(Html.fromHtml(beans.get(groupPosition).getTitle()));
holder.status.setText("" + isFinish + "/" + total);
if (isFinish < total) {
holder.status.setTextColor(mContext.getResources().getColor(R.color.main_blue));
} else {
holder.status.setTextColor(mContext.getResources().getColor(R.color.color_main));
}
} catch (Exception e) {
e.printStackTrace();
}
//根据图片URL显示
if (!TextUtils.isEmpty(beans.get(groupPosition).getIcon_url())) {
holder.image.setVisibility(View.VISIBLE);
holder.image.setImageUrl(beans.get(groupPosition).getIcon_url());
} else {
holder.image.setVisibility(View.VISIBLE);
holder.image.setImageResource(R.drawable.common_default);
}
if(isExpanded && STATE_EXPANDED != holder.expandStatus) {
//如果当前项需要展开
ViewHelper.setRotation(holder.right, EXPAND_ROTATE_ANGLE);
holder.expandStatus = STATE_EXPANDED;
} else if(!isExpanded && STATE_IDLE != holder.expandStatus) {
//如果当前项需要收起
ViewHelper.setRotation(holder.right, 0);
holder.expandStatus = STATE_IDLE;
}
return convertView;
}
2)点击分组展开或者收缩动画,根据数据项找到对应的视图,进行动画播放。
public void setSelectedGroup(int selectedGroup) {
this.selectedGroup = selectedGroup;
//获取对应的视图信息
ViewHolderGroup holder = groupInfo.get(selectedGroup);
if (null != holder) {
if (holder.isExpanded) {
//展开分组
expandGroupAnimation(holder);
} else {
//收起分组
collapseGroupAnimation(holder);
}
}
}
/**
* 收起分组动画
*/
private void collapseGroupAnimation(final ViewHolderGroup holder) {
if(STATE_IDLE == holder.expandStatus
|| (holder.animating && STATE_COLLAPSING == holder.expandStatus)) {
return;
}
ObjectAnimator animation = ObjectAnimator.ofFloat(holder.right, "rotation", 90, 0);
holder.expandStatus = STATE_COLLAPSING;
holder.animating = true;
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
selectedGroup = -1;
holder.expandStatus = STATE_IDLE;
holder.animating = false;
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animation.setDuration(250);
animation.start();
}
/**
* 展开分组动画
*/
private void expandGroupAnimation(final ViewHolderGroup holder) {
if (STATE_EXPANDED == holder.expandStatus
|| (holder.animating && STATE_EXPANDING == holder.expandStatus)) {
return;
}
LogUtils.d(TAG, "expandGroupAnimation start: ");
ObjectAnimator animation = ObjectAnimator.ofFloat(holder.right, "rotation", 0, 90);
animation.setDuration(250);
holder.expandStatus = STATE_EXPANDING;
holder.animating = true;
LogUtils.d(TAG, "expandGroupAnimation : ");
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
selectedGroup = -1;
holder.expandStatus = STATE_EXPANDED;
holder.animating = false;
LogUtils.d(TAG, "expandGroupAnimation End: ");
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animation.start();
}
static class ViewHolderGroup {
/**
* 分组标题
*/
TextView title;
/**
* 分组图片
*/
SmartImageView image;
/**
* 分组右边的标记
*/
TextView right;
/**
* 分组完成情况
*/
TextView status;
/**
* 当前状态
*/
int expandStatus = STATE_IDLE;
/**
* 当前分组是否展开
*/
boolean isExpanded;
/**
* 是否正在播放动画
*/
boolean animating;
}
}
3)在列表初始化完成后,再进行动画播放。
mListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
final int groupPosition, long id) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
commonDetailAdapter.setSelectedGroup(groupPosition);
Log.d(CommonDetailAdapter.TAG, "onGroupClick groupPosition : " + groupPosition);
}
}, 100);
return false;
}
});
当然,这只是我的一种处理方法。最完美的方式当前是能够将动画处理完美集成到
ExpandableListView的固有方法和过程中。网络上还有其他一些的方法可参考。