Android自定义View——仿1号店垂直滚动广告条实现

效果图

原理分析

整个过程都是基于坐标Y的增加和交换进行处理的,Y值都会一直增加到endY,然后进行交换逻辑

实现步骤

1、初始化变量

由于1号店是两句话的滚动,所以我们也是使用两句话来实现的

public class VerTextView extends View {

    private Paint mPaint;
    private float x, startY, endY, firstY, nextStartY, secondY;

    //整个View的宽高是以第一个为标准的,所以第二句话长度必须小于第一句话
    private String[] text = {"今日特卖:毛衣3.3折>>>", "公告:全场半价>>>"};
    private float textWidth, textHeight;

    //滚动速度
    private float speech = 0;
    private static final int CHANGE_SPEECH = 0x01;
    //是否已经在滚动
    private boolean isScroll = false;

    public VerTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //初始化画笔
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setTextSize(30);
        //测量文字的宽高,以第一句话为标准
        Rect rect = new Rect();
        mPaint.getTextBounds(text[0], 0, text[0].length(), rect);
        textWidth = rect.width();
        textHeight = rect.height();
        //文字开始的x,y坐标
        //由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
        x = getX() + getPaddingLeft();
        startY = getTop() + textHeight + getPaddingTop() - 5;
        //文字结束的x,y坐标
        endY = startY + textHeight + getPaddingBottom();
        //下一个文字滚动开始的y坐标
        //由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
        nextStartY = getTop() - 5;
        //记录开始的坐标
        firstY = startY;
        secondY = nextStartY;
    }
}

2、获取宽和高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = measureWidth(widthMeasureSpec);
    int height = measureHeight(heightMeasureSpec);
    setMeasuredDimension(width, height);
}

private int measureHeight(int heightMeasureSpec) {
    int result = 0;
    int size = MeasureSpec.getSize(heightMeasureSpec);
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    if (mode == MeasureSpec.EXACTLY) {
        result = size;
    } else {
        result = (int) (getPaddingTop() + getPaddingBottom() + textHeight);
        if (mode == MeasureSpec.AT_MOST) {
            result = Math.min(result, size);
        }
    }
    return result;
}

private int measureWidth(int widthMeasureSpec) {
    int result = 0;
    int size = MeasureSpec.getSize(widthMeasureSpec);
    int mode = MeasureSpec.getMode(widthMeasureSpec);
    if (mode == MeasureSpec.EXACTLY) {
        result = size;
    } else {
        result = (int) (getPaddingLeft() + getPaddingRight() + textWidth);
        if (mode == MeasureSpec.AT_MOST) {
            result = Math.min(result, size);
        }
    }
    return result;
}

3、绘制图形

  • 通过Handler来改变速度
  • 通过isScroll控制Handler只发送一次
  • 通过invalidate一直重绘两句话的文字
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case CHANGE_SPEECH:
                speech = 1f;
                break;
        }
    }
};

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    //启动滚动
    if (!isScroll) {
        mHandler.sendEmptyMessageDelayed(CHANGE_SPEECH, 2000);
        isScroll = true;
    }

    canvas.drawText(text[0], x, startY, mPaint);
    canvas.drawText(text[1], x, nextStartY, mPaint);
    startY += speech;
    nextStartY += speech;

    //超出View的控件时
    if (startY > endY || nextStartY > endY) {
        if (startY > endY) {
            //第一次滚动过后交换值
            startY = secondY;
            nextStartY = firstY;
        } else if (nextStartY > endY) {
            //第二次滚动过后交换值
            startY = firstY;
            nextStartY = secondY;
        }
        speech = 0;
        isScroll = false;
    }
    invalidate();
}

4、监听点击事件

public onTouchListener listener;

public interface onTouchListener {
    void touchListener(String s);
}

public void setListener(onTouchListener listener) {
    this.listener = listener;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            //点击事件
            if (listener != null) {
                if (startY >= firstY && nextStartY < firstY) {
                    listener.touchListener(text[0]);
                } else if (nextStartY >= firstY && startY < firstY) {
                    listener.touchListener(text[1]);
                }
            }
            break;
    }
    return true;
}

5、实现点击事件

public class VerTextViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ver_text_view);

        VerTextView tv_ver = (VerTextView) findViewById(R.id.tv_ver);
        tv_ver.setListener(new VerTextView.onTouchListener() {
            @Override
            public void touchListener(String s) {
                Toast.makeText(VerTextViewActivity.this, s, Toast.LENGTH_LONG).show();
            }
        });
    }
}

6、布局实现

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="120dp"
        android:layout_height="30dp"
        android:background="@drawable/vertextview" />

    <com.handsome.app3.Custom.VerTextView.VerTextView
        android:id="@+id/tv_ver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:padding="8dp" />
</LinearLayout>

7、源码下载

滚动广告View下载

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值