自定義View實戰(zhàn)-仿京東首頁輪播文字(又名垂直跑馬燈)

自定義View實戰(zhàn)-仿京東首頁輪播文字(又名垂直跑馬燈)

京東客戶端的輪播文字效果:

這里寫圖片描述

本次要實現(xiàn)的只是后面滾動的文字(前面的用ImageView或者TextView實現(xiàn)即可),看一下實現(xiàn)的效果:

這里寫圖片描述

經(jīng)過更改后本組件已可以已開源庫的形式添加到項目中使用(不用下載然后導(dǎo)入),使用方法及介紹詳見Github.本文的實例Demo也在這里.ADTextView,歡迎star.

我還寫了另外一個開源庫,多達(dá)288種動畫效果定制的側(cè)滑菜單庫有興趣的點了看一下,歡迎star(不應(yīng)該說歡迎,應(yīng)該說求star,因為快畢業(yè)找工作了,多幾個star簡歷上也好看一下,謝謝了)

關(guān)于開源庫的詳細(xì)介紹可以看這篇博客:多達(dá)288種動態(tài)效果的側(cè)滑菜單開源庫,滿足您項目的各種需求

關(guān)于如何發(fā)布一個開源庫的內(nèi)容可以查看這篇博客:發(fā)布新手的第一個開源庫-快速發(fā)布開源庫到JitPack

好了,接著說垂直跑馬燈的內(nèi)容

實現(xiàn)思路:

這里寫圖片描述
這里寫圖片描述

上圖只是一個大概的思路,要實現(xiàn)還需要完善更多的細(xì)節(jié),下面會一步步的來實現(xiàn)這個效果:

1.封裝數(shù)據(jù)源:從圖上可以看到,輪播的文字是分為兩個部分的,暫且把它們分別叫做前綴和內(nèi)容,而且實際的使用過程中點擊輪播圖肯定是需要跳轉(zhuǎn)頁面的,而且大部分應(yīng)該是WebView,不妨我們就設(shè)置點擊時候需要獲取的內(nèi)容就是一個鏈接,那么數(shù)據(jù)源的結(jié)構(gòu)就很明了了

這里寫圖片描述

創(chuàng)建ADEnity類并設(shè)計參數(shù)完善一些基本的方法,代碼如下

public class ADEnity {
    private String mFront ; //前面的文字
    private String mBack ; //后面的文字
    private String mUrl ;//包含的鏈接

    public ADEnity(String mFront, String mBack,String mUrl) {
        this.mFront = mFront;
        this.mBack = mBack;
        this.mUrl = mUrl;
    }

    public String getmUrl() {
        return mUrl;
    }

    public void setmUrl(String mUrl) {
        this.mUrl = mUrl;
    }

    public String getmFront() {
        return mFront;
    }

    public void setmFront(String mFront) {
        this.mFront = mFront;
    }

    public String getmBack() {
        return mBack;
    }

    public void setmBack(String mBack) {
        this.mBack = mBack;
    }
}

2.接下來應(yīng)該是定制這個自定義View了,首先理一下思路,看一個構(gòu)造圖

這里寫圖片描述

實現(xiàn)這個自定義View的所有參數(shù)都在上表列出了,大部分參數(shù)很容易理解,個別參數(shù)加進(jìn)去是很有必要的,比如說是否初始化進(jìn)入文字的縱坐標(biāo),文字是否在移動中,是否處于停頓狀態(tài),這三個參數(shù),之后的內(nèi)容會詳細(xì)的敘述一下.

在動手繪制之前還得需要知道一點基礎(chǔ)的知識,就是關(guān)于繪制文字的方法,里面有很多細(xì)節(jié)需要處理

首先是畫布繪制文字的方法:

返回值 方法 描述
void drawText(String text, float x, float y, Paint paint) Draw the text, with origin at (x,y), using the specified paint.
void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) Draw the specified range of text, specified by start/end, with its origin at (x,y), in the specified Paint.
void drawText(char[] text, int index, int count, float x, float y, Paint paint) Draw the text, with origin at (x,y), using the specified paint.
void drawText(String text, int start, int end, float x, float y, Paint paint) Draw the text, with origin at (x,y), using the specified paint.

方法都比較好理解,繪制指定字符串(可以指定范圍)在坐標(biāo)( x , y )處,但是其中的x,y并不是我們所理解的應(yīng)該是文字左上角的坐標(biāo)點.其中的x坐標(biāo)是根據(jù)Paint的屬性可變換的,默認(rèn)的x是文字的左邊坐標(biāo),如果Paint設(shè)置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心位置.Y坐標(biāo)是文字的baseliney坐標(biāo).

關(guān)于繪制文字的baseline:

用圖來說話吧

這里寫圖片描述

圖中藍(lán)色的線即為baseline,可以看出他既不是頂部坐標(biāo)也不是底部坐標(biāo),那么當(dāng)我們繪制文字的時候肯定是希望能把文字繪制在正中間.這時候就要引入paint.getTextBound()方法了

getTextBounds(String text, int start, int end, Rect bounds),傳入一個Rect對象,調(diào)用此方法之后則會填充這個rect對象,而填充的內(nèi)容就是所繪制的文字相對于baseline的偏移坐標(biāo),將這個Rect加上baseline的坐標(biāo),繪制后是這樣的:

這里寫圖片描述

但其實他的值只是(2,-25,76,3),是相對于baseline的位置,畫個圖會比較好理解

這里寫圖片描述

那么要將文字繪制在中間,那么實際繪制baseline的坐標(biāo)應(yīng)該是組件的中心,加上文字中心(即圖中框的中間坐標(biāo))相對于baseline的偏移值

這里寫圖片描述

這張圖中應(yīng)該會好理解實際繪制文字的坐標(biāo)與組件中心坐標(biāo)的關(guān)系.關(guān)于偏移值的計算,按常規(guī)的幾何計算方法,應(yīng)該是組件的中心坐標(biāo)+偏移值的絕對值==baseline坐標(biāo)(即實際繪制的坐標(biāo)),但是由于框的坐標(biāo)值都是相對于baseline來計算的,top為負(fù)值,botton為正值,那么這個偏移值就可以直接用(top+bottom)/2的絕對值來表示,沒看懂的同學(xué)可以畫個草圖,用top=-25,bottom=3來算一下,看是否結(jié)果是一致的.

經(jīng)過上面的理解,那我們來繪制正確繪制文字的方法也就確定了

已獲得組件的高度int mHeight , 文字外框Rect bound的情況下

繪制文字在正中間

mHeight  / 2 - (bound.top + bound.bottom) / 2
(因為bound.top + bound.bottom為負(fù)值,所以用減法)
//在縱坐標(biāo)為mY的地方繪制文字
//計算方式
//mheight /2 = mY + (bound.top + bound.bottom) / 2 ;

文字滾動到最高點

mY == 0 - bound.bottom
//在縱坐標(biāo)為mY的地方繪制,此時文字剛好移動到最高點
//計算方式
//mY + bound.bottom = 0 ;

文字滾動到最低點,剛好滾出組件

mY = mHeight  - indexBound.top;
//在縱坐標(biāo)為mY的地方繪制,此時文字剛好移動到最高點
//計算方式
//mY + bound.top = mHeight  ;

知道了如何正確的繪制文字和邊界情況的坐標(biāo)判斷,下面就到了繪制文字的步驟了

書寫自定義View,定義需要用到的屬性,完成構(gòu)造方法

public class ADTextView extends View {
    private int mSpeed; //文字出現(xiàn)或消失的速度 建議1~5
    private int mInterval; //文字停留在中間的時長
    private int mFrontColor; //前綴顏色
    private int mContentColor; //內(nèi)容的顏色
    private int mFrontTextSize; //前綴文字大小
    private int mContentTextSize; //內(nèi)容文字大小

    private List<AdEntity> mTexts; //顯示文字的數(shù)據(jù)源
    private int mY = 0; //文字的Y坐標(biāo)
    private int mIndex = 0; //當(dāng)前的數(shù)據(jù)下標(biāo)
    private Paint mPaintContent; //繪制內(nèi)容的畫筆
    private Paint mPaintFront; //繪制前綴的畫筆
    private boolean isMove = true; //文字是否移動
    private String TAG = "ADTextView";
    private boolean hasInit = false;
    private boolean isPaused = false;

       public ADTextView(Context context) {
        this(context, null);
    }

    public ADTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //獲取資源屬性值
        obtainStyledAttrs(attrs);
        //初始化數(shù)據(jù)
        init();
    }

定義資源文件屬性,values下建立attrs.xml文件

<declare-styleable name="ADTextView">
        <!--文字進(jìn)入與消失的時間-->
        <attr name="ad_text_view_speed" format="integer"/>

        <!--文字停留在中心的時間-->
        <attr name="ad_text_view_interval" format="integer"/>

        <!--前綴文字顏色-->
        <attr name="ad_text_front_color" format="color"/>
        <!--前綴文字大小-->
        <attr name="ad_text_front_size" format="dimension"/>

        <!--內(nèi)容文字顏色-->
        <attr name="ad_text_content_color" format="color"/>
        <!--內(nèi)容文字大小-->
        <attr name="ad_text_content_size" format="dimension"/>
    </declare-styleable>

代碼中獲取資源文件內(nèi)的屬性值,并賦予默認(rèn)值

 //獲取資源文件
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.ADTextView);
        mSpeed = array.getInt(R.styleable.ADTextView_ad_text_view_speed, 1);
        mInterval = array.getInt(R.styleable.ADTextView_ad_text_view_interval, 2000);
        mFrontColor = array.getColor(R.styleable.ADTextView_ad_text_front_color, Color.RED);
        mContentColor = array.getColor(R.styleable.ADTextView_ad_text_content_color, Color.BLACK);
        mFrontTextSize = (int) array.getDimension(R.styleable.ADTextView_ad_text_front_size, SizeUtil.Sp2Px(getContext(), 15));
        mContentTextSize = (int) array.getDimension(R.styleable.ADTextView_ad_text_content_size, SizeUtil.Sp2Px(getContext(), 15));
        array.recycle();
    }

注:設(shè)置默認(rèn)值時用到了尺寸的轉(zhuǎn)換,詳情見這篇博客:自定義View之尺寸的轉(zhuǎn)化

初始化數(shù)據(jù)

//初始化默認(rèn)值
    private void init() {
        mIndex = 0;
        mPaintFront = new Paint();
        mPaintFront.setAntiAlias(true);
        mPaintFront.setDither(true);
        mPaintFront.setTextSize(mFrontTextSize);
        mPaintFront.setColor(mFrontColor);

        mPaintContent = new Paint();
        mPaintContent.setAntiAlias(true);
        mPaintContent.setDither(true);
        mPaintContent.setTextSize(mContentTextSize);
        mPaintContent.setColor(mContentColor);

    }

重寫onMeasure進(jìn)行寬高的測量(細(xì)節(jié)見注釋)

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        //設(shè)置寬高
        setMeasuredDimension(width, height);
    }

    //測量寬度
    private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            result = size; //具體的值
        } else { //高度至少為兩倍字高
            int mfronTextHeight = (int) (mPaintFront.descent() - mPaintFront.ascent()); //前綴文字字高
            int mContentTextHeight = (int) (mPaintContent.descent() - mPaintContent.ascent()); //內(nèi)容文字字高
            result = Math.max(mfronTextHeight, mContentTextHeight) * 2;

            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    //測量高度
    private int measureWidth(int widthMeasureSpec) {
        int result = 0;
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else { //寬度最小十個字的寬度
            String text = "十個字十個字十個字字";
            Rect rect = new Rect();
            mPaintContent.getTextBounds(text, 0, text.length(), rect);
            result = rect.right - rect.left;
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

前面的敘述中我們知道,剛開始進(jìn)入的時候文字應(yīng)該是位于組件的底部的,但是這個值是需要獲取組件的高度和當(dāng)前顯示文字的情況下來判斷的,所以應(yīng)該放在onDraw內(nèi)來初始化這個值,所以需要前面的是否初始化的屬性,判斷當(dāng)mY==0并且未初始化的時候給mY賦值.

接下來就是onDraw內(nèi)的處理

獲取當(dāng)前的數(shù)據(jù)

            ADEnity model = mTexts.get(mIndex);
            String font = model.getmFront();
            String back = model.getmBack();
           

為測量前綴與內(nèi)容的寬度,獲取文字的Rect對象

 //前綴的Bound
            Rect indexBound = new Rect();
            mPaintFront.getTextBounds(font, 0, font.length(), indexBound);

            //內(nèi)容文字的Bound
            Rect contentBound = new Rect();
            mPaintContent.getTextBounds(back, 0, back.length(), contentBound);
            if (mY == 0 && hasInit == false) {
                mY = getMeasuredHeight() - indexBound.top;
                hasInit = true;
            }

mY進(jìn)行初始化

if (mY == 0 && hasInit == false) {
                mY = getMeasuredHeight() - indexBound.top;
                hasInit = true;
            }

繪制文字

  canvas.drawText(back, 0, back.length(), (indexBound.right - indexBound.left) + 20, mY, mPaintContent);
            canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront);

對邊界情況的處理

//移動到最上面
if (mY <= 0 - indexBound.bottom) {
mY = getMeasuredHeight() - indexBound.top; //返回底部
mIndex++; //換下一組數(shù)據(jù)
isPaused = false; //重置暫停狀態(tài)
        }

 //移動到中間
            if (!isPaused && mY <= getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) {
                isMove = false;
                isPaused = true;
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        postInvalidate();
                        isMove = true;
                    }
                }, mInterval);
            }
            mY -= mSpeed;

移動的處理與數(shù)據(jù)源的處理

mY -= mSpeed; //速度即為每次移動的像素值,推薦1~5,這也是前面判斷中間與最上方的時候使用<=的原因,如果每次只移動1像素,使用==完全可以,其他值則有可能跳過==的這個條件,導(dǎo)致不會停頓或者不會循環(huán)
//循環(huán)使用數(shù)據(jù)
if (mIndex == mTexts.size()) {
     mIndex = 0;
}
//如果是處于移動狀態(tài)時的,則延遲繪制
//計算公式為一個比例,一個時間間隔移動組件高度,則多少毫秒來移動1像素
if (isMove) {
  postInvalidateDelayed(mDuration / getMeasuredHeight());
}

至此對邏輯的處理就完成了,接下來要設(shè)置點擊事件

//設(shè)置一個回調(diào)并設(shè)置setXXX方法
public interface onClickLitener {
        public void onClick(String mUrl);
}

private onClickLitener onClickLitener;

public void setOnClickLitener(TextViewAd.onClickLitener onClickLitener) {
        this.onClickLitener = onClickLitener;
}

//重寫onTouchEvent事件,并且要返回true,表明當(dāng)前的點擊事件由這個組件自身來處理
@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (onClickLitener != null) {
         //調(diào)用回調(diào),將當(dāng)前數(shù)據(jù)源的鏈接傳出去         onClickLitener.onClick(mTexts.get(mIndex).getmUrl());
                }

                break;
        }
        return true;
    }

暴露一些其他屬性的設(shè)置方式

 //設(shè)置數(shù)據(jù)源
    public void setmTexts(List<AdEntity> mTexts) {
        this.mTexts = mTexts;
    }

    //設(shè)置廣告文字的停頓時間
    public void setInterval(int mInterval) {
        this.mInterval = mInterval;
    }

    //設(shè)置速度
    public void setSpeed(int spedd) {
        this.mSpeed = spedd;
    }

    //設(shè)置前綴的文字顏色
    public void setFrontColor(int mFrontColor) {
        mPaintFront.setColor(mFrontColor);
    }

    //設(shè)置正文內(nèi)容的顏色
    public void setBackColor(int mBackColor) {
        mPaintContent.setColor(mBackColor);
    }

有興趣的同學(xué)可以將這些屬性設(shè)置到attrs.xml文件中然后就可以在布局文件中設(shè)置屬性了,這里就不演示了,因為覺得每次copy這個View還得把xml文件也copy比較麻煩,畢竟as有自動補全,可以很方便的看到暴露在外面的方法.(個人感受而已).

貼一下完整的ADTextView的代碼,方便查看

package com.brioal.brioallib.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.brioal.brioallib.R;
import com.brioal.brioallib.entity.AdEntity;
import com.brioal.baselib.util.klog.KLog;
import com.brioal.baselib.util.SizeUtil;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 仿京東垂直滾動廣告欄
 * Created by Brioal on 2016/7/22.
 */

public class ADTextView extends View {
    private int mSpeed; //文字出現(xiàn)或消失的速度 建議1~5
    private int mInterval; //文字停留在中間的時長
    private int mFrontColor; //前綴顏色
    private int mContentColor; //內(nèi)容的顏色
    private int mFrontTextSize; //前綴文字大小
    private int mContentTextSize; //內(nèi)容文字大小

    private List<AdEntity> mTexts; //顯示文字的數(shù)據(jù)源
    private int mY = 0; //文字的Y坐標(biāo)
    private int mIndex = 0; //當(dāng)前的數(shù)據(jù)下標(biāo)
    private Paint mPaintContent; //繪制內(nèi)容的畫筆
    private Paint mPaintFront; //繪制前綴的畫筆
    private boolean isMove = true; //文字是否移動
    private String TAG = "ADTextView";
    private boolean hasInit = false;
    private boolean isPaused = false;

    public interface onClickListener {
        public void onClick(String mUrl);
    }

    private onClickListener onClickListener;

    public void setOnClickListener(onClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public ADTextView(Context context) {
        this(context, null);
    }

    public ADTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        obtainStyledAttrs(attrs);
        init();
    }

    //獲取資源文件
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.ADTextView);
        mSpeed = array.getInt(R.styleable.ADTextView_ad_text_view_speed, 1);
        mInterval = array.getInt(R.styleable.ADTextView_ad_text_view_interval, 2000);
        mFrontColor = array.getColor(R.styleable.ADTextView_ad_text_front_color, Color.RED);
        mContentColor = array.getColor(R.styleable.ADTextView_ad_text_content_color, Color.BLACK);
        mFrontTextSize = (int) array.getDimension(R.styleable.ADTextView_ad_text_front_size, SizeUtil.Sp2Px(getContext(), 15));
        mContentTextSize = (int) array.getDimension(R.styleable.ADTextView_ad_text_content_size, SizeUtil.Sp2Px(getContext(), 15));
        array.recycle();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (onClickListener != null) {
                    onClickListener.onClick(mTexts.get(mIndex).getmUrl());
                }

                break;
        }
        return true;
    }

    @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 mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else { //高度至少為兩倍字高
            int mfronTextHeight = (int) (mPaintFront.descent() - mPaintFront.ascent()); //前綴文字字高
            int mContentTextHeight = (int) (mPaintContent.descent() - mPaintContent.ascent()); //內(nèi)容文字字高
            result = Math.max(mfronTextHeight, mContentTextHeight) * 2;

            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    //測量高度
    private int measureWidth(int widthMeasureSpec) {
        int result = 0;
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else { //寬度最小十個字的寬度
            String text = "十個字十個字十個字字";
            Rect rect = new Rect();
            mPaintContent.getTextBounds(text, 0, text.length(), rect);
            result = rect.right - rect.left;
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    //設(shè)置數(shù)據(jù)源
    public void setmTexts(List<AdEntity> mTexts) {
        this.mTexts = mTexts;
    }

    //設(shè)置廣告文字的停頓時間
    public void setInterval(int mInterval) {
        this.mInterval = mInterval;
    }

    //設(shè)置速度
    public void setSpeed(int spedd) {
        this.mSpeed = spedd;
    }

    //設(shè)置前綴的文字顏色
    public void setFrontColor(int mFrontColor) {
        mPaintFront.setColor(mFrontColor);
    }

    //設(shè)置正文內(nèi)容的顏色
    public void setBackColor(int mBackColor) {
        mPaintContent.setColor(mBackColor);
    }

    //初始化默認(rèn)值
    private void init() {
        mIndex = 0;
        mPaintFront = new Paint();
        mPaintFront.setAntiAlias(true);
        mPaintFront.setDither(true);
        mPaintFront.setTextSize(mFrontTextSize);
        mPaintFront.setColor(mFrontColor);

        mPaintContent = new Paint();
        mPaintContent.setAntiAlias(true);
        mPaintContent.setDither(true);
        mPaintContent.setTextSize(mContentTextSize);
        mPaintContent.setColor(mContentColor);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mTexts != null) {
            AdEntity model = mTexts.get(mIndex);
            String font = model.getmFront();
            String back = model.getmBack();
            //繪制前綴
            Rect indexBound = new Rect();
            mPaintFront.getTextBounds(font, 0, font.length(), indexBound);

            //繪制內(nèi)容文字
            Rect contentBound = new Rect();
            mPaintContent.getTextBounds(back, 0, back.length(), contentBound);
            if (mY == 0 && hasInit == false) {
                mY = getMeasuredHeight() - indexBound.top;
                hasInit = true;
            }
            //移動到最上面
            if (mY <= 0 - indexBound.bottom) {
                KLog.i(TAG, "onDraw: " + getMeasuredHeight());
                mY = getMeasuredHeight() - indexBound.top;
                mIndex++;
                isPaused = false;
            }
            canvas.drawText(back, 0, back.length(), (indexBound.right - indexBound.left) + 20, mY, mPaintContent);
            canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront);
            //移動到中間
            if (!isPaused && mY <= getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) {
                isMove = false;
                isPaused = true;
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        postInvalidate();
                        isMove = true;
                    }
                }, mInterval);
            }
            mY -= mSpeed;
            //循環(huán)使用數(shù)據(jù)
            if (mIndex == mTexts.size()) {
                mIndex = 0;
            }
            //如果是處于移動狀態(tài)時的,則延遲繪制
            //計算公式為一個比例,一個時間間隔移動組件高度,則多少毫秒來移動1像素
            if (isMove) {
                postInvalidateDelayed(2);
            }
        }

    }
}

至此這個自定義View就完成了,有不足的地方歡迎指出,另外建了個新手交流Android開發(fā)的QQ群,歡迎加入.

群號:375276053

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芯急,一起剝皮案震驚了整個濱河市娶耍,隨后出現(xiàn)的幾起案子榕酒,更是在濱河造成了極大的恐慌故俐,老刑警劉巖药版,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槽片,死亡現(xiàn)場離奇詭異筐乳,居然都是意外死亡,警方通過查閱死者的電腦和手機氓皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進(jìn)店門股淡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廷区,“玉大人隙轻,你說我怎么就攤上這事×泊桑” “怎么了呐籽?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵狡蝶,是天一觀的道長贪惹。 經(jīng)常有香客問我衙猪,道長,這世上最難降的妖魔是什么丝格? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任预伺,我火速辦了婚禮曼尊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瞒御。我一直安慰自己肴裙,他們只是感情好蜻懦,可當(dāng)我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布宛乃。 她就那樣靜靜地躺著征炼,像睡著了一般吁朦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雄右,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天擂仍,我揣著相機與錄音逢渔,去河邊找鬼肃廓。 笑死,一個胖子當(dāng)著我的面吹牛盲赊,可吹牛的內(nèi)容都是我干的哀蘑。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼合溺,長吁一口氣:“原來是場噩夢啊……” “哼棠赛!你這毒婦竟也來了恭朗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤律罢,失蹤者是張志新(化名)和其女友劉穎棍丐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巾钉,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡砰苍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年赚导,在試婚紗的時候發(fā)現(xiàn)自己被綠了吼旧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圈暗。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖勇哗,靈堂內(nèi)的尸體忽然破棺而出智绸,到底是詐尸還是另有隱情访忿,我是刑警寧澤,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布迹恐,位于F島的核電站殴边,受9級特大地震影響锤岸,放射性物質(zhì)發(fā)生泄漏板乙。R本人自食惡果不足惜募逞,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一放接、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玛瘸,春花似錦捧韵、人聲如沸汉操。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽针炉。三九已至篡帕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拢军,已是汗流浹背怔鳖。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工度陆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留献幔,地道東北人。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓鸿竖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悟泵。 傳聞我的和親對象是個殘疾皇子糕非,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 一禁筏、概述 1. 四線格與基線 小時候衡招,我們在剛開始學(xué)習(xí)寫字母時,用的本子是四線格的州刽,我們必須把字母按照規(guī)則寫在四線...
    addapp閱讀 7,675評論 2 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,336評論 25 707
  • ¥開啟¥ 【iAPP實現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程辨绊,因...
    小菜c閱讀 6,451評論 0 17
  • 前言: 在接觸Android這么長時間门坷,看到很多大牛都在和大家分享自己的知識袍镀,深有體會流椒,剛好前段時間寫了一個Dem...
    楊艷偉閱讀 1,280評論 0 5
  • 好像上班以來睡得就比在學(xué)校的早些呢,早上總是在鬧鐘響之前就醒了惯裕,起來后洗漱收拾蜻势,有時候做點吃的就出門了鹉胖。往公司來的...
    倚馬看落日閱讀 203評論 0 0