實現 Android TextView 文字輪播效果

啥叫文字輪播,不廢話看圖:android

這是咱們 app 項目中經常遇到的需求了,通常你們都是找找別人寫好的第三方控件來用的,我也是這樣。可是隨着時間長河的流逝,咱們也必須相應的成長, 文字輪播這種常見的東西但是不能錯過的面試

我找一些實現,總結下實現思路:canvas

1.ViewAnimator 思路
使用 ViewAnimator 自身特性,對期中的子 view 實現動畫切換性能優化

2.自定義 viewGroup 思路
在這個思路下,咱們自定義一個容器,繼承 FrameLayout ,根據數據數量本身 new 相應數量的 itemView 出來加入 FrameLayout ,動畫是經過對當前 itemView 作一個出去的佛納甘話,同時對下一個 itemView 作一個進入動畫,使用 handle 實現延遲輪換架構

3.ViewFlipper 思路
ViewFlipper 思路和 ViewAnimator 同樣,不過 ViewFlipper 使用上更靈活,這裏咱們根據數據流量動態往 ViewFlipper 裏添加 itemViewapp

4.TextSwitcher 思路
這個不想寫了,沒啥意思,TextSwitcher 不熟的你們本身去看看好了,這裏我貼一下參考實現得了:ide

  • Android文字自動輪播實現

TextSwitcher 不熟悉的看這個:佈局

  • TextSwitcher 實現 android 公告欄

5.自定義 textView 思路
其實這個思路也好理解,咱們繼承 textView  ,而後在 onDraw 繪製中本身話文字,本身作動畫,動畫的思路是先把上一個文字上移到頂,而後再繪製下一個文字,從下面開始一直移動到中間post

免費獲取安卓開發架構的資料(包括Fultter、高級UI、性能優化、架構師課程、 NDK、Kotlin、混合式開發(ReactNative+Weex)和一線互聯網公司關於android面試的題目彙總能夠加:936332305 / 連接:點擊連接加入【安卓開發架構】

ViewAnimator 思路

ViewAnimator 是個 viewGroup ,能夠實現動畫切換其中子 view 的效果。在 xml 佈局種,咱們把 ViewAnimator 當一個容器,裏面寫輪播的 view,寫多少個 view 就有多少個輪播,而後設置切換的動畫,用 handle 作定時延遲輪播,調 ViewAnimator.onNext 就能夠切換到下一個 view性能

推薦參考實現:

  • android文字輪播——ViewAnimator實現

實現思路:

1.先在 layout  xml 中聲明佈局層級結構:

     <ViewAnimator
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="歡迎"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="測試"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="本程序"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="!!!!!"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="hello,world"/>
    </ViewAnimator>

2.代碼種設置切換動畫

viewAnimator.setOutAnimation(this, R.anim.slide_out_up);
viewAnimator.setInAnimation(this, R.anim.slide_in_down);

3.handle 延遲循環顯示下一個

   public void showNext() {
        viewAnimator.showNext();
    }

    public void showPrevious() {
        viewAnimator.showPrevious();
    }

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (autoPlayFlag) {
                showNext();
            }
            handler.sendMessageDelayed(new Message(), TIME_INTERVAL);
        }
    };

咱們在須要的位置發送 handle 事件就能夠了

使用 ViewAnimator 有點和肯定一樣明顯

  • 優勢:使用簡單,沒有難度
  • 缺點:xml 中固定了 view個數,爲了兼容不一樣數據量,咱們須要再 ViewAnimator 基礎上 2 次開發

這裏有個例子就是繼承 ViewAnimator 作了一個帶切換動畫的 button,思路不錯

  • ViewAnimator實現按鈕切換動畫

自定義 viewGroup 思路

在這個思路下,咱們自定義一個容器,繼承 FrameLayout ,根據數據數量本身 new 相應數量的 itemView 出來加入 FrameLayout ,動畫是經過對當前 itemView 作一個出去的佛納甘話,同時對下一個 itemView 作一個進入動畫,使用 handle 實現延遲輪換

推薦參考實現:

  • Android:實現一個帶動畫輪播效果的公告條

思路以下

1.在設置數據時添加相應數量的 itemView 進去

public void setNoticeList(List<String> list) {

        // 建立TextView
        for (int i = 0; i < list.size(); i++) {
            TextView textView = createTextView(list.get(i));
            mNoticeList.add(textView);
            addView(textView);
        }
        // 顯示第一條公告
        mCurrentNotice = 0;
        mNoticeList.get(mCurrentNotice).setVisibility(VISIBLE);
        // 啓動輪播
        start();
    }

    private TextView createTextView(String text) {
        if (mLayoutParams == null) {
            mLayoutParams = new LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            mLayoutParams.gravity = Gravity.CENTER_VERTICAL;
        }

        TextView textView = new TextView(getContext());
        textView.setLayoutParams(mLayoutParams);
        textView.setSingleLine();
        textView.setEllipsize(TextUtils.TruncateAt.END);
        textView.setTextColor(mTextColor);
        textView.setVisibility(GONE);
        textView.setText(text);
        // 若是有設置字體大小,若是字體大小爲null。
        if (mTextSize > 0) {
            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        }
        return textView;
    }

2.在 handle 裏面啓動 itemView 切換的動畫

  class NoticeRunnable implements Runnable {
        @Override
        public void run() {
            // 隱藏當前的textView
            TextView currentView = mNoticeList.get(mCurrentNotice);
            currentView.setVisibility(GONE);
            if(mExitAnimSet != null) {
                currentView.startAnimation(mExitAnimSet);
            }
            mCurrentNotice++;
            if(mCurrentNotice >= mNoticeList.size()) {
                mCurrentNotice = 0;
            }

            // 顯示下一個TextView
            TextView nextView = mNoticeList.get(mCurrentNotice);
            nextView.setVisibility(VISIBLE);
            if(mEnterAnimSet != null) {
                nextView.startAnimation(mEnterAnimSet);
            }
            mHandler.postDelayed(this, mNoticeDuration);
        }
    }

    private void createEnterAnimation() {
        mEnterAnimSet = new AnimationSet(false);
        TranslateAnimation translateAnimation =
                new TranslateAnimation(0,0,0,0, TranslateAnimation.RELATIVE_TO_PARENT, 1f,
                        TranslateAnimation.RELATIVE_TO_SELF, 0f);
        AlphaAnimation alphaAnimation = new AlphaAnimation(0f,1f);
        mEnterAnimSet.addAnimation(translateAnimation);
        mEnterAnimSet.addAnimation(alphaAnimation);
        mEnterAnimSet.setDuration(DEFAULT_ANIMATION_DURATION);
    }

    private void createExitAnimation() {
        mExitAnimSet = new AnimationSet(false);
        TranslateAnimation translateAnimation =
                new TranslateAnimation(0,0,0,0, TranslateAnimation.RELATIVE_TO_SELF, 0f,
                        TranslateAnimation.RELATIVE_TO_PARENT, -1f);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1f,0f);
        mExitAnimSet.addAnimation(translateAnimation);
        mExitAnimSet.addAnimation(alphaAnimation);
        mExitAnimSet.setDuration(DEFAULT_ANIMATION_DURATION);
    }

這樣寫最練手,可是我是不推薦這樣乾的,基礎差一些的容易出問題,並且 google 給咱們提供了一些實現,咱們何須非的本身實現呢,反正這樣寫會花點時間

ViewFlipper 思路

ViewFlipper 思路像是上面 1 和 2 的結合,ViewFlipper 對動畫的控制更優秀一些,咱們往 ViewFlipper  裏面動態添加 itemView ,基本都是這個思路,區別是使用的容器不一樣

這裏推薦一個成熟的庫:

  • TextBannerView

這個庫很是完善了,也能知足你們的經常使用需求,是能夠拿來直接用的,你們看圖就明白了

思路以下

他這裏自定義了一個 ViewGroup 繼承自 RelativeLayout,在 view 初始化時添加了一個 ViewFlipper 進來,以後操做的都是這個 ViewFlipper 了

1.自定義 ViewGroup 初始化時添加了 ViewFlipper

    /**初始化控件*/
    private void init(Context context, AttributeSet attrs, int defStyleAttr) {

        mViewFlipper = new ViewFlipper(getContext());//new 一個ViewAnimator
        mViewFlipper.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        addView(mViewFlipper);
        startViewAnimator();
        //設置點擊事件
        mViewFlipper.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = mViewFlipper.getDisplayedChild();//當前顯示的子視圖的索引位置
                if (mListener!=null){
                    mListener.onItemClick(mDatas.get(position),position);
                }
            }
        });

2.根據數據添加 itemView

/**設置數據集合*/
    public void setDatas(List<String> datas){
        this.mDatas = datas;
        if (DisplayUtils.notEmpty(mDatas)){
            mViewFlipper.removeAllViews();
            for (int i = 0; i < mDatas.size(); i++) {
                TextView textView = new TextView(getContext());
                textView.setText(mDatas.get(i));
                //任意設置你的文字樣式,在這裏
                textView.setSingleLine(isSingleLine);
                textView.setTextColor(mTextColor);
                textView.setTextSize(mTextSize);
                textView.setGravity(mGravity);

                mViewFlipper.addView(textView,i);//添加子view,並標識子view位置
            }
        }
    }

3.添加動畫

     /**
     * 設置進入動畫和離開動畫
     *
     * @param inAnimResId  進入動畫的resID
     * @param outAnimResID 離開動畫的resID
     */
    private void setInAndOutAnimation(@AnimRes int inAnimResId, @AnimRes int outAnimResID) {
        Animation inAnim = AnimationUtils.loadAnimation(getContext(), inAnimResId);
        inAnim.setDuration(animDuration);
        mViewFlipper.setInAnimation(inAnim);

        Animation outAnim = AnimationUtils.loadAnimation(getContext(), outAnimResID);
        outAnim.setDuration(animDuration);
        mViewFlipper.setOutAnimation(outAnim);
    }

以後就是用 handle 來作延遲循環,上面複製好幾遍了,這裏是在不想再複製了,打個源碼很簡單,你們直接看。

吐槽下:這個庫多了一道手,多加了一個視圖層級出來,其實不必在頂層加一個 viewGroup 了,直接繼承 ViewFlipper 可好

自定義 textView 思路

不繼承 textView  咱們直接繼承 view 均可以,只要不支持 wrap_content 就好辦。 核心就是在 onDraw 中實現繪製的動畫。

例子這裏沒有使用 ValueAnimator 動畫,而是 1 個 px 變化就重繪一次,性能上欠考慮。

參考資料地址:

  • Android仿京東首頁輪播文字(又名垂直跑馬燈)

實現思路

1.根據文字,肯定文字出屏幕的零界點

// 獲取文字矩陣的尺寸
Rect indexBound = new Rect();
mPaint.getTextBounds(text, 0, text.length(), indexBound);

// 文字居中繪製 Y 的座標
my = mHeight  / 2 - (bound.top + bound.bottom) / 2

// 文字移動到最頂部
mY == 0 - bound.bottom

// 文字移動到最下部
mY = mHeight  - indexBound.top;

2.在 onDraw 中實現繪製

        // 文字首先繪製在最底部,mY 初始時 = 0
        if (mY == 0) {
            mY = getMeasuredHeight() - indexBound.top;
        }

        // 文字移動到最頂部時,更換數據,把文字移動到最底部
        if (mY == 0 - indexBound.bottom) {
            Log.i(TAG, "onDraw: " + getMeasuredHeight());
            mY = getMeasuredHeight() - indexBound.top;//返回底部
            mIndex++;//換下一組數據
        }

        // 文字移動到中間時,中止 handle 的重繪任務,延遲標準時間後再開始
        if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) {
            isMove = false;//中止移動
            // handle 通知標準時間後開始重繪
        }

        // 處理完 Y 座標,開始繪製文字
        canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront);

        // 最後移動 1 個 px,以實現動畫效果
        mY -= 1

做者這裏強調每移動 1個 px 就重繪一遍是爲了確保動畫的連貫性,可是這樣系統也是 16 ms 才繪製一幀的,這樣高頻率的重繪並非最優選擇哦

詳細的去看做何的文章吧,我更喜歡用 ValueAnimator 實現,這樣更貼合 UI 線程的刷新頻率。

說下感想,純本身 draw 繪製的話效率過低,代碼容易出錯不說,如果需求換個方向對於咱們也是一個大麻煩。

免費獲取安卓開發架構的資料(包括Fultter、高級UI、性能優化、架構師課程、 NDK、Kotlin、混合式開發(ReactNative+Weex)和一線互聯網公司關於android面試的題目彙總能夠加:936332305 / 連接:點擊連接加入【安卓開發架構】

相關文章
相關標籤/搜索