前言
快过年了,对于大伙来说手头上的事情做完没有呢,马上也该让自己轻松一阵子了,哈哈哈。好,说正事,由于公司App这个版本首页的改版,新增了很多新的控件,类似于京东快报这种控件的话我在写之前也去找了一下轮子,但是遗憾的是并没有发现简单易用的哈,所以就琢磨自己来实现。
实现思路
快报功能其实也就文字上下滚动,然后给用户一个简单的文字提示,我并不想做太复杂, 而且咱家的产品大大也不需要很复杂的快报功能,所以我就想做个极简的吧。
大致控件需要包括以下几点功能:
1. 文字能上下滚动(自动)
2. 能有点击事件
3. 貌似没了~~~
那先看看效果图
文字的滚动效果实现
首先的话,咱们先把文字画出来吧,用canvas.drawText
固然没错,但是首先要解决一个问题就是说怎么让文字上下居中呢?(如果自己有经常用canvas.drawText的话那肯定熟悉)我们的思路是先画矩形,具体请看代码
mRect = new Rect(20, 20, getMeasuredWidth()-20, getMeasuredHeight()-20);
Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
mY = (mRect.bottom + mRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
其实上面的mY就是我们绘制文字的Y坐标了。
那么然后让文字滚动起来,我这边是通过启动一个计时器,看代码:
@Override
protected void onDraw(final Canvas canvas) {
if(data != null){
if(mY == 0&&!isMove){
mRect = new Rect(20, 20, getMeasuredWidth()-20, getMeasuredHeight()-20);
Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
mY = (mRect.bottom + mRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
nY = mY;
}
Item item = data.get(mIndex);
String text = item.getName();
canvas.drawText(text,mRect.left, nY, textPaint);
if(!isStart){
isStart = true;
Timer timer = new Timer();
countingDown = mInterval+mDuration;
timer.schedule(new TimerTask() {
@Override
public void run() {
countingDown -=20;
if(countingDown<=0) {
countingDown = mInterval + mDuration;
isMove = true;
}
if(countingDown<=mInterval-40&&countingDown>0){
isMove = false;
drawTextStill();
}
if(isMove) {
drawTextMove();
}
}
},mInterval,20);
}
}
}
private void drawTextStill(){
nY = mY;
postInvalidate();
}
private void drawTextMove(){
nY -= getMeasuredHeight()/(mDuration/20);
if(nY<0){
mIndex++;
if(mIndex == data.size())
mIndex = 0;
nY = getMeasuredHeight();
}
postInvalidate();
}
点击事件实现
这个就更加简单啦~
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (onClickListener != null) {
onClickListener.onClick(data.get(mIndex));
}
break;
}
return true;
}
然后开放一些其余的接口
/**
* 设置广告文字的停顿时间
* */
public void setIntervalTime(int mInterval) {
this.mInterval = mInterval;
}
/**
* 设置文字从出现到消失的时长
* */
public void setDurationTime(int mDuration) {
this.mDuration = mDuration;
}
/**
* 设置文字颜色
* */
public void setNoticeColor(int mFrontColor) {
textPaint.setColor(mFrontColor);
}
全部代码
最后贴一下全部代码,其实就一个自定义控件
public class ENoticeView extends View {
private String TAG = "Blin ENoticeView";
private List<Item> data; //显示文字的数据源
private int mIndex = 0; //当前的数据下标
private int mDuration = 400; //文字从出现到显示消失的时间
private int mInterval = 3000; //文字停留在中间的时长切换的间隔
private boolean isMove = false; //文字是否移动
private boolean isStart = false; //是否开始
private int mY = 0; //文字的初始Y坐标
private int nY = 0; //文字的Y坐标
private Paint textPaint;
private final int TEXT_COLOR = 0xff333333;
private final int TEXT_SIZE = 12;
private Rect mRect;
private TimerTask timerTask;
public ENoticeView(Context context) {
super(context);
init();
}
public ENoticeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ENoticeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setDither(true);
textPaint.setColor(TEXT_COLOR);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
textPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,TEXT_SIZE,metrics));
}
@Override
protected void onDraw(final Canvas canvas) {
if(data != null){
if(mY == 0&&!isMove){
mRect = new Rect(20, 20, getMeasuredWidth()-20, getMeasuredHeight()-20);
Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
mY = (mRect.bottom + mRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
nY = mY;
}
Item item = data.get(mIndex);
String text = item.getName();
canvas.drawText(text,mRect.left, nY, textPaint);
if(!isStart){
isStart = true;
Timer timer = new Timer();
countingDown = mInterval+mDuration;
timer.schedule(new TimerTask() {
@Override
public void run() {
countingDown -=20;
if(countingDown<=0) {
countingDown = mInterval + mDuration;
isMove = true;
}
if(countingDown<=mInterval-40&&countingDown>0){
isMove = false;
drawTextStill();
}
if(isMove) {
drawTextMove();
}
}
},mInterval,20);
}
}
}
private long countingDown = 0;
private void drawTextStill(){
nY = mY;
postInvalidate();
}
private void drawTextMove(){
nY -= getMeasuredHeight()/(mDuration/20);
if(nY<0){
mIndex++;
if(mIndex == data.size())
mIndex = 0;
nY = getMeasuredHeight();
}
postInvalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (onClickListener != null) {
onClickListener.onClick(data.get(mIndex));
}
break;
}
return true;
}
public interface OnNoticeClickListener {
public void onClick(Item item);
}
private OnNoticeClickListener onClickListener;
public void setOnNoticeClickListener(OnNoticeClickListener onClickListener) {
this.onClickListener = onClickListener;
}
/**
* 设置数据源
* */
public void setData(List<Item> data) {
this.data = data;
invalidate();
}
/**
* 设置数据源,并且重置
* */
public void setData(List<Item> data,boolean isReStart) {
this.data = data;
if (isReStart)
mIndex = 0;
invalidate();
}
/**
* 设置广告文字的停顿时间
* */
public void setIntervalTime(int mInterval) {
this.mInterval = mInterval;
}
/**
* 设置文字从出现到消失的时长
* */
public void setDurationTime(int mDuration) {
this.mDuration = mDuration;
}
/**
* 设置文字颜色
* */
public void setNoticeColor(int mFrontColor) {
textPaint.setColor(mFrontColor);
}
}
用法
eNoticeView = (ENoticeView) findViewById(R.id.noticeView);
ArrayList<Item> items = new ArrayList<>();
for(int i = 0;i<5;i++){
Item item = new Item();
item.setName("比比大哥测试:"+i);
items.add(item);
}
eNoticeView.setData(items);
eNoticeView.setOnNoticeClickListener(new ENoticeView.OnNoticeClickListener() {
@Override
public void onClick(Item item) {
Toast.makeText(MainActivity.this,item.getName(),Toast.LENGTH_SHORT).show();
}
});
总结
东西真的很简单,绘制的东西也非常少,难点在于上面的计时器绘制循环,这边需要仔细思考下的,然后控制一下子线程,经过测试应该没有什么大问题哈,唯一可能存在的问题就是如果你在RecyclerView中用到的话,如果控件被释放了,要怎么去取消timerTask的问题喽,思考一下呗。