开题贴上这张图,相信我不解释今天分享的内容,大家也已经知道了,接下来我就结合代码为大家讲解一下,这个消息自动轮播VIEW的具体实现方式。
需求分析:
1.在ITEM上随着一条消息滑出之后,下一条消息自动滑入。
2.消息轮播可循环
3.ITEM上绑定点击事件,点击对应的ITEM,TOAST相对应的消息内容。
实现分析:
自定义VIEW继承自FrameLayout,利用Animation动画赋予前后两条被轮播的消息ITEM一个执行滑进、另一条执行滑出,然后,利用FrameLayout的bringChildToFront(待播放的VIEW),循环让被轮播的ITEM位于所有子VIEW之前。
1.准备消息ITEM
Item分析:左边为特定的IMG标签,中间是消息提示的具体内容,右边提示更多箭头。 根据分析,Item上,左边Img标签,中间 文字内容是可变的,右边箭头不变,抽取Item实体类如下:
MessageEntity:
/**
* desc :消息实体
* author:xiedong
* data:2018/7/20
*/
public class MessageEntity {
private String message;
private int imgRes;
public MessageEntity(int imgRes, String message) {
this.message = message;
this.imgRes = imgRes;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getImgRes() {
return imgRes;
}
public void setImgRes(int imgRes) {
this.imgRes = imgRes;
}
}
2.Item实现:
由于整个Item内容比较单一,我采用TextView实现,左右图标用setCompoundDrawables()实现
tipView.setCompoundDrawables(loadDrawable(tip.getImgRes()), null, loadDrawable(R.drawable.ic_more), null);
3.准备Item
Item的滑进、滑出动画,当前可被用户看见的消息执行由下往上滑动动画,待滑入的消息执行由下往上滑进动画,滑进后被用户可见后,执行滑出动画,下一条继续执行滑入动画,变成可见消息继续滑出,待下一条滑入........
3.1更新当前的消息并执行动画
private void updateTipAndPlayAnimation() {
if (curTipIndex % 2 == 0) {
updateTip(tv_tip_out);
tv_tip_in.startAnimation(anim_out);
tv_tip_out.startAnimation(anim_in);
this.bringChildToFront(tv_tip_in);
} else {
updateTip(tv_tip_in);
tv_tip_out.startAnimation(anim_out);
tv_tip_in.startAnimation(anim_in);
this.bringChildToFront(tv_tip_out);
}
}
private void updateTip(TextView tipView) {
final MessageEntity tip = getNextTip();
tipView.setCompoundDrawables(loadDrawable(tip.getImgRes()), null, loadDrawable(R.drawable.ic_more), null);
if (!TextUtils.isEmpty(tip.getMessage())) {
tipView.setText(tip.getMessage());
tipView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onClick(tip.getMessage());
}
});
}
}
3.2从集合中取得下一条被轮播的消息
private MessageEntity getNextTip() {
if (isListEmpty(tipList)) return null;
return tipList.get(curTipIndex++ % tipList.size());
}
3.3动画的具体创建过程
private Animation newAnimation(float fromYValue, float toYValue) {
Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, fromYValue, Animation.RELATIVE_TO_SELF, toYValue);
anim.setDuration(ANIM_DURATION);
anim.setStartOffset(ANIM_DELAYED_MILLIONS);
anim.setInterpolator(new DecelerateInterpolator());
return anim;
}
消息轮播VIEW的核心实现思想如上,主要是结合Animation动画配合FrameLayout的特性实现View的滑进滑出效果,上述分析我贴的是部分核心实现代码,下面我把完整的代码贴上,大家可根据具体业务场景自行扩展。
轮播View代码:
/**
* desc :自定义view实现上下轮播的view(客户端消息轮播效果)
* author:xiedong
* data:2018/7/20
*/
public class LooperMessageView extends FrameLayout {
private List tipList;
private int curTipIndex = 0;
private long lastTimeMillis;
private static final int ANIM_DELAYED_MILLIONS = 3 * 1000;
/**
* 动画持续时长
*/
private static final int ANIM_DURATION = 1 * 1000;
private static final String DEFAULT_TEXT_COLOR = "#2F4F4F";
private static final int DEFAULT_TEXT_SIZE = 16;
private TextView tv_tip_out, tv_tip_in;
private Animation anim_out, anim_in;
private OnItemClickListener onItemClickListener;
public LooperMessageView(Context context) {
this(context, null);
}
public LooperMessageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LooperMessageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTipFrame();
initAnimation();
}
private void initTipFrame() {
tv_tip_out = newTextView();
tv_tip_in = newTextView();
addView(tv_tip_in);
addView(tv_tip_out);
}
private TextView newTextView() {
TextView textView = new TextView(getContext());
LayoutParams lp = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER_VERTICAL);
textView.setLayoutParams(lp);
textView.setCompoundDrawablePadding(10);
textView.setGravity(Gravity.CENTER_VERTICAL);
textView.setLines(2);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setTextColor(Color.parseColor(DEFAULT_TEXT_COLOR));
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE);
return textView;
}
/**
* 将资源图片转换为Drawable对象
*
* @param ResId
* @return
*/
private Drawable loadDrawable(int ResId) {
Drawable drawable = getResources().getDrawable(ResId);
drawable.setBounds(0, 0, drawable.getMinimumWidth() / 4, drawable.getMinimumHeight() / 4);
return drawable;
}
private void initAnimation() {
anim_out = newAnimation(0, -1);
anim_in = newAnimation(1, 0);
anim_in.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
updateTipAndPlayAnimationWithCheck();
}
});
}
private Animation newAnimation(float fromYValue, float toYValue) {
Animation anim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, fromYValue, Animation.RELATIVE_TO_SELF, toYValue);
anim.setDuration(ANIM_DURATION);
anim.setStartOffset(ANIM_DELAYED_MILLIONS);
anim.setInterpolator(new DecelerateInterpolator());
return anim;
}
private void updateTipAndPlayAnimationWithCheck() {
if (System.currentTimeMillis() - lastTimeMillis < 1000) {
return;
}
lastTimeMillis = System.currentTimeMillis();
updateTipAndPlayAnimation();
}
private void updateTipAndPlayAnimation() {
if (curTipIndex % 2 == 0) {
updateTip(tv_tip_out);
tv_tip_in.startAnimation(anim_out);
tv_tip_out.startAnimation(anim_in);
this.bringChildToFront(tv_tip_in);
} else {
updateTip(tv_tip_in);
tv_tip_out.startAnimation(anim_out);
tv_tip_in.startAnimation(anim_in);
this.bringChildToFront(tv_tip_out);
}
}
private void updateTip(TextView tipView) {
final MessageEntity tip = getNextTip();
tipView.setCompoundDrawables(loadDrawable(tip.getImgRes()), null, loadDrawable(R.drawable.ic_more), null);
if (!TextUtils.isEmpty(tip.getMessage())) {
tipView.setText(tip.getMessage());
tipView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onClick(tip.getMessage());
}
});
}
}
/**
* 获取下一条消息
*
* @return
*/
private MessageEntity getNextTip() {
if (isListEmpty(tipList)) return null;
return tipList.get(curTipIndex++ % tipList.size());
}
public static boolean isListEmpty(List list) {
return list == null || list.isEmpty();
}
public void setTipList(List tipList) {
this.tipList = tipList;
curTipIndex = 0;
updateTip(tv_tip_out);
updateTipAndPlayAnimation();
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
interface OnItemClickListener {
void onClick(String message);
}
}
在布局中引入:
在Activity中,绑定被轮播的消息集合:
public class MainActivity extends AppCompatActivity {
private LooperMessageView messageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
messageView = (LooperMessageView) findViewById(R.id.lmv_news);
messageView.setTipList(generateTips());
messageView.setOnItemClickListener(new LooperMessageView.OnItemClickListener() {
@Override
public void onClick(String message) {
Toast.makeText(MainActivity.this, "正要跳转到 " + message, Toast.LENGTH_SHORT).show();
}
});
}
private List generateTips() {
List tips = new ArrayList<>();
tips.add(new MessageEntity(R.drawable.ic_friends, "有小伙伴艾特你了"));
tips.add(new MessageEntity(R.drawable.ic_friend_b, "社区里有人给你发私信了"));
return tips;
}
}
项目源码已上传github,欢迎拍砖。自定义view实现上下轮播的view(客户端消息提醒)