效果图展示,图片有点卡,耐心看会,原程序是很流畅的
实现步骤:
实现原理(坐标变换原理):整个过程都是基于坐标y的增加和交换进行处理的,y值都会一直增加到endy,然后进行交换逻辑
步骤一:声明变量
由于1号店是两句话的滚动,所以我们也是使用两句话来实现的
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;
步骤二:初始化画笔、文本大小和坐标
以第一句话为标准来做控件的宽高标准
//初始化画笔 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;
步骤三:onmeasure()适配wrap_content的宽高
如果学习过自定义view的话,下面的代码应该很熟悉,就是适配warp_content的模板代码:
@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; }
步骤四:ondraw()画出根据坐标画出两段text(已修复:text停下来时闪一下的bug)
通过handler来改变速度
通过isscroll锁,来控制handler只改变一次
通过invalidate一直重绘两句话的文字
@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(); } private handler mhandler = new handler() { @override public void handlemessage(message msg) { super.handlemessage(msg); switch (msg.what) { case change_speech: speech = 1f; break; } } };
步骤五:监听点击事件(已修复:点击事件错乱的问题)
在自定义view重写dispatchtouchevent处理点击事件,这个也是模板代码:
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; }
步骤六:在activity中实现点击事件
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(); } }); } }
布局文件
<?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>
整个类的源码:
/** * =====作者===== * 许英俊 * =====时间===== * 2016/10/11. */ 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; private handler mhandler = new handler() { @override public void handlemessage(message msg) { super.handlemessage(msg); switch (msg.what) { case change_speech: speech = 1f; break; } } }; 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; } @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; } @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(); } 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; } }
以上所述是小编给大家介绍的android自定义view实现仿1号店垂直滚动广告条代码,希望对大家有所帮助
如对本文有疑问, 点击进行留言回复!!
LongClick原理、上下文菜单原理、EditText长按弹窗原理、WebView长按弹窗自定义、修复WebView全选重复bug ———————————————— 版权声明:本文为CSDN博主「
JobScheduler 实现 特定时间,特定条件(系统空闲,电池电量,磁盘空间 ……)下执行任务
网友评论