实现 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 / 连接:点击连接加入【安卓开发架构】

相关文章
相关标签/搜索