Android自定義View實現(xiàn)仿1號店垂直滾動廣告條代碼
效果圖展示,圖片有點卡,耐心看會,原程序是很流暢的
實現(xiàn)步驟:
- 聲明變量
- 初始化畫筆、文本大小和坐標(biāo)
- onMeasure()適配wrap_content的寬高
- onDraw()畫出根據(jù)坐標(biāo)畫出兩段Text
- 監(jiān)聽點擊事件
- 在Activity中實現(xiàn)點擊事件
實現(xiàn)原理(坐標(biāo)變換原理):整個過程都是基于坐標(biāo)Y的增加和交換進(jìn)行處理的,Y值都會一直增加到endY,然后進(jìn)行交換邏輯
步驟一:聲明變量
由于1號店是兩句話的滾動,所以我們也是使用兩句話來實現(xiàn)的
private Paint mPaint; private float x, startY, endY, firstY, nextStartY, secondY; //整個View的寬高是以第一個為標(biāo)準(zhǔn)的,所以第二句話長度必須小于第一句話 private String[] text = {"今日特賣:毛衣3.3折>>>", "公告:全場半價>>>"}; private float textWidth, textHeight; //滾動速度 private float speech = 0; private static final int CHANGE_SPEECH = 0x01; //是否已經(jīng)在滾動 private boolean isScroll = false;
步驟二:初始化畫筆、文本大小和坐標(biāo)
以第一句話為標(biāo)準(zhǔn)來做控件的寬高標(biāo)準(zhǔn)
//初始化畫筆 mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setTextSize(30); //測量文字的寬高,以第一句話為標(biāo)準(zhǔn) Rect rect = new Rect(); mPaint.getTextBounds(text[0], 0, text[0].length(), rect); textWidth = rect.width(); textHeight = rect.height(); //文字開始的x,y坐標(biāo) //由于文字是以基準(zhǔn)線為基線的,文字底部會突出一點,所以向上收5px x = getX() + getPaddingLeft(); startY = getTop() + textHeight + getPaddingTop() - 5; //文字結(jié)束的x,y坐標(biāo) endY = startY + textHeight + getPaddingBottom(); //下一個文字滾動開始的y坐標(biāo) //由于文字是以基準(zhǔn)線為基線的,文字底部會突出一點,所以向上收5px nextStartY = getTop() - 5; //記錄開始的坐標(biāo) firstY = startY; secondY = nextStartY;
步驟三:onMeasure()適配wrap_content的寬高
如果學(xué)習(xí)過自定義View的話,下面的代碼應(yīng)該很熟悉,就是適配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()畫出根據(jù)坐標(biāo)畫出兩段Text(已修復(fù):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; } } };
步驟五:監(jiān)聽點擊事件(已修復(fù):點擊事件錯亂的問題)
在自定義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中實現(xiàn)點擊事件
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的寬高是以第一個為標(biāo)準(zhǔn)的,所以第二句話長度必須小于第一句話 private String[] text = {"今日特賣:毛衣3.3折>>>", "公告:全場半價>>>"}; private float textWidth, textHeight; //滾動速度 private float speech = 0; private static final int CHANGE_SPEECH = 0x01; //是否已經(jīng)在滾動 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); //測量文字的寬高,以第一句話為標(biāo)準(zhǔn) Rect rect = new Rect(); mPaint.getTextBounds(text[0], 0, text[0].length(), rect); textWidth = rect.width(); textHeight = rect.height(); //文字開始的x,y坐標(biāo) //由于文字是以基準(zhǔn)線為基線的,文字底部會突出一點,所以向上收5px x = getX() + getPaddingLeft(); startY = getTop() + textHeight + getPaddingTop() - 5; //文字結(jié)束的x,y坐標(biāo) endY = startY + textHeight + getPaddingBottom(); //下一個文字滾動開始的y坐標(biāo) //由于文字是以基準(zhǔn)線為基線的,文字底部會突出一點,所以向上收5px nextStartY = getTop() - 5; //記錄開始的坐標(biāo) 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實現(xiàn)仿1號店垂直滾動廣告條代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android中監(jiān)聽系統(tǒng)網(wǎng)絡(luò)連接打開或者關(guān)閉的實現(xiàn)代碼
本篇文章對Android中監(jiān)聽系統(tǒng)網(wǎng)絡(luò)連接打開或者關(guān)閉的實現(xiàn)用實例進(jìn)行了介紹。需要的朋友參考下2013-05-05Android使用AsyncQueryHandler實現(xiàn)獲取手機(jī)聯(lián)系人功能
這篇文章主要為大家詳細(xì)介紹了Android使用AsyncQueryHandler實現(xiàn)獲取手機(jī)聯(lián)系人功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Android開發(fā)實現(xiàn)Switch控件修改樣式功能示例【附源碼下載】
這篇文章主要介紹了Android開發(fā)實現(xiàn)Switch控件修改樣式功能,涉及Android Switch開關(guān)控件樣式設(shè)置與事件響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下2019-04-04