首页 > 代码库 > Android--最强跑马灯

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




Android--最强跑马灯