Android仿京東垂直跑馬燈效果

        項目用到了垂直跑馬燈效果,搜了一下之後大佬們實現方式各不相同,我用了一個自定義View重寫TextView的方法,在使用的過程中發現了一些作者的漏洞,也就是挖的坑,我對這個做了修改。實現起來很容易;


        原文連接

        修改1:給屬性添加一些默認值;

        原作中是通過代碼給定一些基礎設置,我這裏很簡單的添加了默認屬性,比如滾動頭的顏色和滾動體的顏色,滾動事件間隔等等;

        修改2:字體大小適配;

        在這裏我針對不同版本的Api(19以上和19以下),做出了區分,設置字體時根據版本不同設置不同的字體型號;並且提供了設置字體大小的方法;

        修改3:模型類內置;

        我將所需要的模型類內置到了控件中,集中在一個文件中,提高內聚;

        修改4:異步刷新數據;

        原作中並沒有對View執行刷新操作,如果加載網絡數據時,加載數據的過程中控件已經完成了初始化,加載的數據不會顯示。我在setmTexts方法中添加的刷新View的方法,使控件數據可以實時刷新修改,數據源發生變化時只需要重新設定數據源即可完成控件刷新操作;

       代碼部分:

package com.souqu.healthbook.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

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

/**
 * Summarize:文字滾動的效果
 *          仿京東首頁跑馬燈效果(改)
 * User:賈恆飛
 * Date:2018/3/30
 * Time:19:56
 * Email:[email protected]
 * Created with AndroidStudio3.0
 */

public class RollTextView extends android.support.v7.widget.AppCompatTextView {
    private int mDuration = 1000; //文字從出現到顯示消失的時間(默認爲1000)
    private int mInterval = 1000; //文字停留在中間的時長切換的間隔(默認爲1000)
    private List<PaoMaModel> mTexts; //顯示文字的數據源
    private int mY = 0; //文字的Y座標
    private int mIndex = 0; //當前的數據下標
    private Paint mPaintBack; //繪製內容的畫筆
    private Paint mPaintFront; //繪製前綴的畫筆
    private boolean isMove = true; //文字是否移動
    private String TAG = "ADTextView";
    private boolean hasInit = false;//是否初始化剛進入時候文字的縱座標

    private int frontSize = 30;//字體大小
    private int backSize = 30;//字體大小

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

    private onClickLitener onClickLitener;

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

    public RollTextView(Context context) {
        super(context);
    }

    public RollTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

    //設置數據源
    public void setmTexts(List mTexts) {
        this.mTexts = mTexts;
        this.invalidate();//刷新控件
    }

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

    //設置文字從出現到消失的時長
    public void setmDuration(int mDuration) {
        this.mDuration = mDuration;
    }

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

    //設置正文內容的顏色
    public void setBackColor(int mBackColor) {
        mPaintBack.setColor(mBackColor);
    }

    //設置前綴的文字大小
    public void setFrontSize(int size) {
        this.frontSize = size;
    }

    //設置正文內容的大小
    public void setBackSize(int size) {
        this.backSize = size;
    }

    //初始化默認值
    private void init() {
        mDuration = 500;
        mInterval = 1000;
        mIndex = 0;
        mPaintFront = new Paint();
        mPaintFront.setAntiAlias(true);
        mPaintFront.setDither(true);
        mPaintFront.setColor(Color.RED);


        mPaintBack = new Paint();
        mPaintBack.setAntiAlias(true);
        mPaintBack.setDither(true);
        mPaintBack.setColor(Color.BLACK);

        //根據SDK來優化字體大小
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            //設置選項卡字體大小SDK>19的情況
            mPaintFront.setTextSize(frontSize+15);
            mPaintBack.setTextSize(backSize+15);
        }else{
            //設置選項卡字體大小SDK<19的情況
            mPaintFront.setTextSize(frontSize);
            mPaintBack.setTextSize(backSize);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.i(TAG, "onSizeChanged: " + h);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mTexts != null&& mTexts.size() > 0) {
            Log.i(TAG, "onDraw: " + mY);
            //獲取當前的數據
            PaoMaModel model = mTexts.get(mIndex);
            String font = model.getmFront();
            String back = model.getmBack();
            // 繪製前綴的外框
            Rect indexBound = new Rect();
            mPaintFront.getTextBounds(font, 0, font.length(), indexBound);

            //繪製內容的外框
            Rect contentBound = new Rect();
            mPaintBack.getTextBounds(back, 0, back.length(), contentBound);
            //剛開始進入的時候文字應該是位於組件的底部的 ,但是這個值是需要獲取組件的高度和當前顯示文字的情況下來判斷的,
            // 所以應該放在onDraw內來初始化這個值,所以需要前面的是否初始化的屬性,判斷當mY==0並且未初始化的時候給mY賦值.
            if (mY == 0 && hasInit == false) {
                mY = getMeasuredHeight() - indexBound.top;
                hasInit = true;
            }
            //移動到最上面
            if (mY == 0 - indexBound.bottom) {
                Log.i(TAG, "onDraw: " + getMeasuredHeight());
                mY = getMeasuredHeight() - indexBound.top;//返回底部
                mIndex++;//換下一組數據
            }
            canvas.drawText(back, 0, back.length(), (indexBound.right - indexBound.left) + 20, mY, mPaintBack);
            canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront);
            //移動到中間
            if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) {
                isMove = false;//停止移動
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        postInvalidate();//通知重繪
                        isMove = true;//設置移動爲true
                    }
                }, mInterval);//停頓多少毫秒之後再次移動
            }
            //移動的處理與數據源的處理
            mY -= 1;//每次只移動一個像素,儘量保證平滑顯示
            //循環使用數據
            if (mIndex == mTexts.size()) {
                mIndex = 0;
            }
            //如果是處於移動狀態時的,則延遲繪製
            //計算公式爲一個比例,一個時間間隔移動組件高度,則多少毫秒來移動1像素
            if (isMove) {
                postInvalidateDelayed(mDuration / getMeasuredHeight());
            }
        }

    }

    /**
     * 構建模型類
     */
    public static class PaoMaModel{
        private String mFront ; //前面的文字
        private String mBack ; //後面的文字
        private String mUrl ;//包含的鏈接

        public PaoMaModel(String mFront, String mBack, String mUrl) {
            this.mFront = mFront;
            this.mBack = "  "+mBack;
            this.mUrl = mUrl;
        }

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

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

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

        public String getmFront() {
            return mFront;
        }

        public String getmBack() {
            return mBack;
        }

        public String getmUrl() {
            return mUrl;
        }
    }

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章