Android--最强跑马灯

Android 跑马灯已经有很多版本,从最基本的TextView,到重写TextView使TextView取消焦点限制,还有重写TextView利用ScrollTo方法写的,基本都能满足一般需要。然而在使用过程中,发现一些意外---有时会不播放,刷新线程还在继续但就是不播放,最后在github上找到一个用动画实现跑马灯的项目(项目地址:https://github.com/ened/Android-MarqueeView,再次对作者表示感谢),改造了一番,总算ok了。以后再也不用为跑马灯烦恼了。

特点:

1. 文字长短都有跑马灯效果。

2. 可以控制速度

代码:

package com.example.test_marquee;

import android.content.Context;
import android.graphics.Paint;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

/**
 * LinearLayout作为父View,必须有一个子TextView
 *
 * 利用动画实现
 */
public class MarqueeView extends LinearLayout {

	private static final int TEXTVIEW_VIRTUAL_WIDTH = 2000;/* TextView默认宽度 */
	private static final int DEFAULT_SPEED = 35;/* 默认滚动速度 越大滚动越慢 */
	private static final int DEFAULT_ANIMATION_PAUSE = 0;/* 出去动画与进入动画的时间间隔 */
	private static final String TAG = MarqueeView.class.getSimpleName();

	private TextView mTextField;/* 该跑马灯的孙子View之TextView */
	private ScrollView mScrollView;/* 该跑马灯的子View之mScrollView */

	private Animation mMoveTextOut = null;/* 作用于TextView的动画 --出去 */
	private Animation mMoveTextIn = null;/* 作用于TextView的动画 --进入 */

	private Paint mPaint;
	private int mSpeed = DEFAULT_SPEED;
	private int mAnimationPause = DEFAULT_ANIMATION_PAUSE;
	private Interpolator mInterpolator = new LinearInterpolator();

	private Runnable mAnimationStartRunnable;

	/** 字符串之间的间隔 */
	private String interval = "     ";
	private String stringOfItem = "";
	/** str+interval的长度 */
	private float widthOfItem = 0;
	private float widthOfTextView;
	private String stringOfTextView = "";
	private float startXOfOut = 0;
	private float endXOfOut = 0;
	private float startXOfIn = 0;
	private float endXOfIn = 0;

	public MarqueeView(Context context) {
		super(context);
		init(context);
	}

	public MarqueeView(Context context, AttributeSet attrs) {
		super(context, attrs);

		init(context);
	}

	public MarqueeView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	private void init(Context context) {
		// init helper
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setStrokeWidth(1);
		mPaint.setStrokeCap(Paint.Cap.ROUND);
		mInterpolator = new LinearInterpolator();
	}

	// 当给子View分配位置和尺寸时调用。
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		Logcat.d(TAG, "onLayout called!" + "changed: " + changed);
		if (getChildCount() == 0 || getChildCount() > 1) {
			throw new RuntimeException(
					"MarqueeView must have exactly one child element.");
		}

		//
		if (changed) {
			View v = getChildAt(0);
			if (!(v instanceof TextView)) {
				throw new RuntimeException(
						"The child view of this MarqueeView must be a TextView instance.");
			}
			initView(getContext());
			mTextField.setText(mTextField.getText());
		}
	}

	/** Starts the configured marquee effect. */
	public void startMarquee() {
		Logcat.d(TAG, "startMarquee called");
		startTextFieldAnimation();
	}

	// 一旦开始动画,动画结束开始由监听器负责。
	private void startTextFieldAnimation() {
		mAnimationStartRunnable = new Runnable() {
			public void run() {
				mTextField.startAnimation(mMoveTextOut);
			}
		};
		postDelayed(mAnimationStartRunnable, mAnimationPause);
	}

	/**
	 * Disables the animations.
	 */
	public void reset() {

		if (mAnimationStartRunnable == null)
			return;
		removeCallbacks(mAnimationStartRunnable);
		mTextField.clearAnimation();
		mMoveTextOut.reset();
		mMoveTextIn.reset();
		invalidate();
	}

	private void prepareAnimation() {
		// Measure
		mPaint.setTextSize(mTextField.getTextSize());
		mPaint.setTypeface(mTextField.getTypeface());
		float mTextWidth = mPaint.measureText(mTextField.getText().toString());

		float width = getMeasuredWidth();
		startXOfOut = -(mTextWidth - width) % widthOfItem;
		endXOfOut = -mTextWidth + width;
		startXOfIn = -(mTextWidth - width) % widthOfItem;
		endXOfIn = -mTextWidth + width;

		final int duration = ((int) Math.abs(startXOfOut - endXOfOut) * mSpeed);

		if (BuildConfig.DEBUG) {
			Log.d(TAG, "(int) Math.abs(startXOfOut - endXOfOut)       : "
					+ (int) Math.abs(startXOfOut - endXOfOut));
			Log.d(TAG, "mSpeed       : " + mSpeed);
			Log.d(TAG, "startXOfOut       : " + startXOfOut);
			Log.d(TAG, "endXOfOut         : " + endXOfOut);
			Log.d(TAG, "startXOfIn        : " + startXOfIn);
			Log.d(TAG, "endXOfIn  		  : " + endXOfIn);
			Log.d(TAG, "duration  		  : " + duration);
		}
		mMoveTextOut = new TranslateAnimation(startXOfOut, endXOfOut, 0, 0);
		mMoveTextOut.setDuration(duration);
		mMoveTextOut.setInterpolator(mInterpolator);
		mMoveTextOut.setFillAfter(true);

		mMoveTextIn = new TranslateAnimation(startXOfIn, endXOfIn, 0, 0);
		mMoveTextIn.setDuration(duration);
		mMoveTextIn.setStartOffset(mAnimationPause);
		mMoveTextIn.setInterpolator(mInterpolator);
		mMoveTextIn.setFillAfter(true);

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

			public void onAnimationEnd(Animation animation) {
				mTextField.startAnimation(mMoveTextIn);
			}

			public void onAnimationRepeat(Animation animation) {
			}
		});

		mMoveTextIn.setAnimationListener(new Animation.AnimationListener() {
			public void onAnimationStart(Animation animation) {
			}

			public void onAnimationEnd(Animation animation) {
				startTextFieldAnimation();
			}

			public void onAnimationRepeat(Animation animation) {
			}
		});
	}

	/** 初始化子View */
	private void initView(Context context) {

		// Scroll View
		LayoutParams sv1lp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		sv1lp.gravity = Gravity.CENTER_HORIZONTAL;
		mScrollView = new ScrollView(context);

		// Scroll View 1 - Text Field
		mTextField = (TextView) getChildAt(0);
		removeView(mTextField);

		mScrollView.addView(mTextField, new ScrollView.LayoutParams(
				TEXTVIEW_VIRTUAL_WIDTH, LayoutParams.WRAP_CONTENT));

		mTextField.addTextChangedListener(new TextWatcher() {
			@Override
			public void beforeTextChanged(CharSequence charSequence, int i,
					int i2, int i3) {
			}

			@Override
			public void onTextChanged(CharSequence charSequence, int i, int i2,
					int i3) {
			}

			@Override
			public void afterTextChanged(Editable editable) {

				Logcat.d(TAG, "afterTextChanged called");

				// 如果提供的字符串未被加工过,就先加工,否则就开始动画
				if (!stringOfTextView.equals(editable.toString())) {

					String str = editable.toString();
					mPaint.setTextSize(mTextField.getTextSize());
					mPaint.setTypeface(mTextField.getTypeface());

					stringOfItem = str + interval;
					widthOfItem = mPaint.measureText(stringOfItem);
					stringOfTextView = stringOfItem;
					widthOfTextView = widthOfItem;

					while (widthOfTextView <= 2 * getMeasuredWidth()) {
						stringOfTextView += stringOfItem;
						widthOfTextView = mPaint.measureText(stringOfTextView);
					}
					Logcat.d(TAG, "string of TextView deal ok!###");
					Logcat.d(TAG, "lengthOfll: " + getMeasuredWidth() + "###");
					Logcat.d(TAG, "lengthOfTextView: " + widthOfTextView
							+ "###");
					Logcat.d(TAG, "CONTENT: " + stringOfTextView + "###");
					// 设置起始
					mTextField.setText(stringOfTextView);
					return;
				}
				reset();
				prepareAnimation();
				expandTextView();
				post(new Runnable() {
					@Override
					public void run() {
						startMarquee();
					}
				});
			}
		});

		addView(mScrollView, sv1lp);
	}

	private void expandTextView() {
		ViewGroup.LayoutParams lp = mTextField.getLayoutParams();
		lp.width = (int) widthOfTextView + 5;
		mTextField.setLayoutParams(lp);
	}
}

使用:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <com.example.test_marquee.MarqueeView
        android:id="@+id/marqueeView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/tv_marquee"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:ellipsize="end"
            android:singleLine="true"
            android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do."
            android:textSize="20sp"
            android:textStyle="bold" />
    </com.example.test_marquee.MarqueeView>
</RelativeLayout>

Demo下载地址:

http://download.csdn.net/detail/u012587637/8219987

时间: 2024-10-15 17:18:18

Android--最强跑马灯的相关文章

android TextView实现跑马灯效果(字体滚动)

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" androi

android 怎么实现跑马灯效果

自定义控件 FocusedTextView, 使android系统误以为它拥有焦点 1 public class FocusedTextView extends TextView { 2 public FocusedTextView(Context context, AttributeSet attrs, int defStyle) { 3 super(context, attrs, defStyle); 4 // TODO Auto-generated constructor stub 5 }

Android:TextView文字跑马灯的效果实现

解决TextView文字显示不全的问题. 简单设置跑马灯的效果: <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="marquee" android:focusable="true" androi

android ViewPager实现 跑马灯切换图片+多种切换动画

近期在弄个项目.要求有跑马灯效果的图片展示. 网上搜了一堆,都没有完美实现的算了还是自己写吧! 实现原理利用 ViewPager 控件,这个控件本身就支持滑动翻页非常好非常强大好多功能都能用上它.利用mViewPager.setCurrentItem(currentIndex); 来实现切换当前显示的view 在加一个定时器不断设置setCurrentItem 来实现跑马灯效果. 一.主要实现类 凝视非常具体了 一看就知道了 package com.example.marqueeimage; i

android 自定义文字跑马灯 支持拖拽,按住停止滚动,自定义速度

android的textview自带跑马灯效果,一般使用足够了.不过也有不一般的情况,所以我实现了一个自定义textview控件,用来针对这种不一般情况下的跑马灯效果实现. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

android开发_ListView跑马灯

Focus on technology, enjoy life!-- QQ:804212028 浏览链接:http://blog.csdn.net/y18334702058/article/details/44624305 主题:ListView跑马灯 -TextView之所以需要跑马灯,是由于文字太长,或者是为了展现这种效果. 关键代码: android:singleLine="true" android:ellipsize="marquee" android:f

android textview 实现跑马灯效果

跑马灯效果最重要的就是四个属性,分别是: android:ellipsize="marquee" android:focusable="true" android:focusableInTouchMode="true" android:singleLine="true" 控件的宽度,不一定是具体的值,可以是math_parent,如果想让textview中的文字滚动的话,那里面内容的长度肯定是要大于控件的长度的,不然滚动还有啥

Android TextView实现跑马灯

TextView实现跑马灯的效果:例子一: 这个例子可以解决给一个TextView实现跑马灯的效果,但是不能解决给所有的TextView实现跑马灯的效果. <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" android:ellipsize="marquee&quo

Android 跑马灯效果

<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="5dp" android:text="我是一只小小小小鸟,怎么飞也飞不高啊,我有一个梦想,正在很努力地去实现" android:singleLine="true" android:ellipsize=&q