[Android] 不一样的走马灯,MarqueeManualView自定义走马灯效果的Android控件

众所周知,当TextView需要文字进入走马灯状态的时候,需要设置属性

android:ellipsize="marquee"

但是有时候并不能启动走马灯效果,这是因为失去了焦点导致的,这时候可以通过强制设置焦点,从而促使不会不启动走马灯,同时如果没有启动走马灯,设置setSelected(true)也会开启走马灯效果,

当然我们也可以重写Textview强制开启走马灯。

以下是我自定义的一个强制开启走马灯的TextView  ,  MarqueeFocusedTextView

package com.bluewindtalker.weight;

/**
 * 
 */

import android.content.Context;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * @author bluewindtalker
 * @description 防止由于焦点问题而不能自动启动的走马灯textview,这里建议加上以下属性。
 *      android:singleLine="true"
 *       android:focusableInTouchMode="true"
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:focusable="true"
        
        代码效果相同。(另:正常的textview如果没有自动启动,可以通过设置setSelected(true)开启走马灯)
        如需要高级使用,请查看MarqueeManualView
 * @data Jun 22, 2016 -- 2:56:11 PM
 * @reference http://m15031966454.blog.163.com/blog/static/201261060201222043240815/
 */
public class MarqueeFocusedTextView extends TextView {
  
  /**
   * @param context
   */
  public MarqueeFocusedTextView(Context context) {
    this(context, null);
  }

  /**
   * @param context
   * @param attrs
   */
  public MarqueeFocusedTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initMarquee();
  }
  
  /**
   * @param context
   * @param attrs
   * @param defStyleAttr
   */
  public MarqueeFocusedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initMarquee();
  }
  
  @Override
  public boolean isFocused() {
    //跑马灯, 只有当焦点在它上面时才有效。
    return true;
  }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        // 关于MarqueeText类中为什么要复写onFocusChanged()方法,那是因为如果不写,在Textview 获得焦点后,再失
        //去焦点时 字就会停止“跑”了,所以如果想让它一直跑下去就复写onFocusChanged(),并且里面什么也不做(主要是不能调用父类的方法)
        //http://m15031966454.blog.163.com/blog/static/201261060201222043240815/
//    super.onFocusChanged(focused, direction, previouslyFocusedRect);
    }


    /**
     * 初始化,设置必要的属性
     */
    private void initMarquee(){
        setFocusable(true);
        setSingleLine();
        setFocusableInTouchMode(true);
        setEllipsize(TextUtils.TruncateAt.MARQUEE);
        setMarqueeRepeatLimit(-1);
    }
}

但是这种原生的TextView衍生出的走马灯存在 以下问题

无法设置走马灯文字之间间隔;

无法设置走马灯速度;

无法设置走马灯启动、关闭时机;

无法设置走马灯暂停的时间长短;


这些都是在TextView中设置好的,例如间距以下是TextView源码

void start(int repeatLimit) {
            if (repeatLimit == 0) {
                stop();
                return;
            }
            mRepeatLimit = repeatLimit;
            final TextView textView = mView.get();
            if (textView != null && textView.mLayout != null) {
                mStatus = MARQUEE_STARTING;
                mScroll = 0.0f;
                final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() -
                        textView.getCompoundPaddingRight();
                final float lineWidth = textView.mLayout.getLineWidth(0);
                final float gap = textWidth / 3.0f;//这里将间距设固定了,没法通过反射等方式更改
                mGhostStart = lineWidth - textWidth + gap;
                mMaxScroll = mGhostStart + textWidth;
                mGhostOffset = lineWidth + gap;
                mFadeStop = lineWidth + textWidth / 6.0f;
                mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;

                textView.invalidate();
                mChoreographer.postFrameCallback(mStartCallback);
            }
        }
另外走马灯的启动时间、间隔时间、速度都是固定的,有兴趣的童鞋可以去查看下TextView源码

	private static final int MARQUEE_DELAY = 1200;
        private static final int MARQUEE_RESTART_DELAY = 1200;
        private static final int MARQUEE_DP_PER_SECOND = 30;


如果简单的认为复制粘贴TextView源码重新再写一份TextView即可,那就大错特错了,因为TextView引用了隐藏的类,无法获取对应的类。这时候就需要我们重新写一个自定义的走马灯控件,来实现以上功能。

这个控件名字是MarqueeManualView

主要思想就是通过一个FrameLayout循环调用两个内容一样的TextView实现循环调用。


这个mMoveTextOut是当前展示的TextView从屏幕显示的区域移动到看不见的区域

	mMoveTextOut = new TranslateAnimation(0, -mTextDifference, 0, 0);
        mMoveTextOut.setDuration(marqueeDuration);
        mMoveTextOut.setInterpolator(mInterpolator);
        mMoveTextOut.setFillAfter(true);

        mMoveTextOut.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                if (isStop) {
                    return;
                }
                postDelayed(mMarqueeRunnable, mPauseTime);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });


这个mMoveTextIn是TextView从屏幕外,移动到屏幕中view的起点的动画


       mMoveTextIn = new TranslateAnimation(mTextDifference, 0, 0, 0);
        mMoveTextIn.setDuration(marqueeDuration);
        mMoveTextIn.setInterpolator(mInterpolator);
        mMoveTextIn.setFillAfter(true);
 

通过以上两个TextView走的动画进行循环,


具体步骤:

1.mTextView使用动画mMoveTextOut,而mGhostTextView使用动画mMoveTextIn,

2.当mGhostTextView到达mTextView初始位置的时候,则停顿下,交替两个控件的动画,mGHostTextView使用动画mMoveTextOut,而mTextView使用动画mMoveTextIn.

重复上面1,2.


运行效果:



控件全部代码及对应示例代码如下:

https://github.com/bluewindtalker/MarqueeManualView







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值