android 滚动字幕

来源:互联网 发布:视频直播app源码 编辑:程序博客网 时间:2024/04/29 17:50


最近项目需求做个循环滚动字幕功能,自己找了相关资料,分别实现了垂直滚动和水平滚动方式,根据自己的风格用两种方法实现了该功能;


2015-12-15更新:以前实现效果并不理想(文字的格式排版,自定义颜色,大小字号等TextView原有属性都不能很好兼容实现),在Github上找了一个较好的实现,并对此进行修改改进处理,这个支持所有TextView属性,还支持速度

实现原理:垂直滚动textview加ScrollView实现,水平滚动textview加HorizontalScrollView实现

具体实现:

import android.content.Context;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Handler;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.util.Log;import android.view.Gravity;import android.view.LayoutInflater;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.TextView;public class TextMarqueeView {private Context mContext = null;private TextView txtView = null;private View vroot = null;private Animation mMoveTextOut = null;// private Animation mMoveTextIn = null;private Animation mMoveTextUp = null;// private Animation mMoveTextDown=null;private boolean mMarqueeNeeded = false;private static final String TAG = "TextMarqueeView112";private static final int TEXTVIEW_VIRTUAL_WIDTH = 990000;private static final int TEXTVIEW_HEIGHT = 990000;/** 由左到右滚动 */public static final int SCROLL_HORIZONTAL_LEFT = 111;/** 由下到上滚动 */public static final int SCROLL_VERTICAL_UP = 112;private int currentScrollDirection = SCROLL_HORIZONTAL_LEFT;public int getCurrentScrollDirection() {return currentScrollDirection;}public void setCurrentScrollDirection(int currentScrollDirection) {this.currentScrollDirection = currentScrollDirection;}/** * Control the speed. The lower this value, the faster it will scroll. */public static final int SPEED_NORMAL = 20;public static final int SPEED_FAST = 12;public static final int SPEED_LOW = 30;/** * Control the pause between the animations. Also, after starting this * activity. */private static final int DEFAULT_ANIMATION_PAUSE = 1000;private int mSpeedHorizontal = SPEED_NORMAL;private int mSpeedVertical = SPEED_NORMAL * 2;private int mAnimationPause = DEFAULT_ANIMATION_PAUSE;private Interpolator mInterpolator = new LinearInterpolator();private boolean mCancelled = false;private Runnable mAnimationStartRunnable;private boolean mStarted = false;private int currentSrollMode = TextMarqueeView.SCROLL_HORIZONTAL_LEFT;public TextMarqueeView(Context context) {this.mContext = context;intView();}public void setTextSpeed(int speedMode) {if (speedMode == SPEED_FAST) {this.mSpeedHorizontal = SPEED_FAST;this.mSpeedVertical = SPEED_FAST * 2;} else if (speedMode == SPEED_LOW) {this.mSpeedHorizontal = SPEED_LOW;this.mSpeedVertical = SPEED_LOW * 2;} else {this.mSpeedHorizontal = SPEED_NORMAL;this.mSpeedVertical = SPEED_NORMAL * 2;}}private ITextPlayEnd endLis;public void setEndListner(ITextPlayEnd endLis) {this.endLis = endLis;}public interface ITextPlayEnd {public void playEnd();}private void intView() {LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);if (currentScrollDirection == TextMarqueeView.SCROLL_HORIZONTAL_LEFT) {vroot = inflater.inflate(R.layout.text_horizontal_scroll, null);txtView = (TextView) vroot.findViewById(R.id.textViewHorizontal);} else if (currentScrollDirection == TextMarqueeView.SCROLL_VERTICAL_UP) {vroot = inflater.inflate(R.layout.text_vertical_scroll, null);txtView = (TextView) vroot.findViewById(R.id.textViewVertical);}}protected View getCreateView() {return vroot;}protected void setTextContent(String txt) {txtView.setText(txt);}public TextView getElevatorWidgetView() {return txtView;}public void startMarquee() { // Log.d(TAG, "startMarquee()");if (this.currentSrollMode == TextMarqueeView.SCROLL_HORIZONTAL_LEFT) {if (mMarqueeNeeded && mMoveTextOut != null)startHorizontalAnimation();} else if (this.currentSrollMode == TextMarqueeView.SCROLL_VERTICAL_UP) {if (mMarqueeNeeded && mMoveTextUp != null)startVerticalAnimation();}mCancelled = false;mStarted = true;}private void startHorizontalAnimation() {mAnimationStartRunnable = new Runnable() {@Overridepublic void run() {Log.d(TAG, "startHorizontalAnimation");txtView.startAnimation(mMoveTextOut);}};vroot.postDelayed(mAnimationStartRunnable, mAnimationPause);}private void startVerticalAnimation() {mAnimationStartRunnable = new Runnable() {@Overridepublic void run() {Log.d(TAG, "startVerticalAnimation");txtView.startAnimation(mMoveTextUp);}};vroot.postDelayed(mAnimationStartRunnable, mAnimationPause);}/** * Disables the animations. */public void reset() {mCancelled = true;if (mAnimationStartRunnable != null) {vroot.removeCallbacks(mAnimationStartRunnable);}txtView.clearAnimation();mStarted = false;if (mMoveTextOut != null)mMoveTextOut.reset();if (mMoveTextUp != null)mMoveTextUp.reset();// mAnimationStartRunnable=null;vroot.invalidate();}private void prepareAnimation() {String text = txtView.getText().toString(); // Log.d(TAG, "text content="+text);if (TextUtils.isEmpty(text))return;Paint mPaint = txtView.getPaint();mPaint.setTextSize(txtView.getTextSize());mPaint.setTypeface(txtView.getTypeface());Rect mBounds = new Rect();mPaint.getTextBounds(text, 0, text.length(), mBounds);float textWidth = mBounds.width();float textHeight = mBounds.height();// Log.d(TAG, "Rect calucate w h="+textWidth+" :"+textHeight);// Log.d(TAG, "measuredWidth    : " +// vroot.getMeasuredWidth()+" measuredHeight :"+vroot.getMeasuredHeight());float padTextWidth = mPaint.measureText(text, 0, 1);/* * float mTextWidth = * mPaint.measureText(mTextField.getText().toString()); Log.d(TAG, * "mTextWidth       : " + mTextWidth); */if (currentSrollMode == TextMarqueeView.SCROLL_HORIZONTAL_LEFT) {mMarqueeNeeded = textWidth > vroot.getMeasuredWidth();// 垂直居中坐标int centerPosY = Math.round(vroot.getMeasuredHeight() / 2- textHeight / 2);int duration = (int) Math.ceil(textWidth * mSpeedHorizontal);initHorizontalAnim(vroot.getMeasuredWidth() - padTextWidth,-(textWidth + padTextWidth), centerPosY, duration);// Log.d(TAG,// "get w h left top="+vroot.getWidth()+" "+vroot.getHeight()+" "+vroot.getLeft()+" "+vroot.getTop());// initHorizontalAnim(getLeft()+getMeasuredWidth()/2,getLeft(),8*1000);} else if (currentSrollMode == TextMarqueeView.SCROLL_VERTICAL_UP) {// int lines=txtView.getLineCount();int lines = (int) Math.ceil(textWidth / vroot.getMeasuredHeight());mMarqueeNeeded = lines * textHeight > vroot.getMeasuredHeight();// mTextField.getHeight();// Log.d(TAG, "verticalView line count="+lines);// Log.d(TAG,// " getView w h"+txtView.getWidth()+", "+txtView.getHeight());// 滚动完最后一行// lines++;float moveHeight = lines * textHeight;int duration = (int) Math.ceil(moveHeight * mSpeedVertical);// Log.d(TAG, "moveDistance="+moveHeight);initVerticalAnim(vroot.getMeasuredHeight() + textHeight * 2,-(moveHeight - textHeight * 3), duration);//}}private void initHorizontalAnim(float fromX, float toX, float posY,int duration) {// ,float locationY// Log.d(TAG, "initHorizontalAnim fromx="+fromX+" toX="+toX);mMoveTextOut = new TranslateAnimation(fromX, toX, posY, posY);mMoveTextOut.setDuration(duration);mMoveTextOut.setInterpolator(mInterpolator);mMoveTextOut.setFillAfter(true);mMoveTextOut.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) { // Log.d(TAG, "mMoveTextOut_onAnimationStart");expandHorizontalTextView();}@Overridepublic void onAnimationEnd(Animation animation) { // Log.d(TAG, "mMoveTextOut_animEnd");cutHorizontalTextView();if (endLis != null)endLis.playEnd();if (mCancelled) {return;}startHorizontalAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});}private void initVerticalAnim(float fromY, float toY, int duration) {mMoveTextUp = new TranslateAnimation(0, 0, fromY, toY);mMoveTextUp.setDuration(duration);mMoveTextUp.setInterpolator(mInterpolator);mMoveTextUp.setFillAfter(true);mMoveTextUp.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {// Log.d(TAG, "mMoveTextUp_onAnimationStart");expandVerticalTextView();}@Overridepublic void onAnimationEnd(Animation animation) { // Log.d(TAG, "mMoveTextUp_AnimEnd");cutVerticalTextView();if (endLis != null)endLis.playEnd();if (mCancelled) {return;}startVerticalAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});}private void initHorizontalView() {txtView.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence charSequence, int i,int i2, int i3) {}@Overridepublic void onTextChanged(CharSequence charSequence, int i, int i2,int i3) {}@Overridepublic void afterTextChanged(Editable editable) { // Log.d(TAG, "afterTextChanged");final boolean continueAnimation = mStarted;reset();prepareAnimation();cutHorizontalTextView();/* * if (continueAnimation) { Log.d(TAG, * "TextChange Restart play"); startMarquee(); } */vroot.post(new Runnable() {@Overridepublic void run() {if (continueAnimation) { // Log.d(TAG, "TextChange Restart play");startMarquee();}}});}});}private void expandHorizontalTextView() {ViewGroup.LayoutParams lp = txtView.getLayoutParams();lp.width = TEXTVIEW_VIRTUAL_WIDTH;txtView.setLayoutParams(lp);}private void cutHorizontalTextView() {if (txtView.getWidth() != vroot.getMeasuredWidth()) {ViewGroup.LayoutParams lp = txtView.getLayoutParams();lp.width = vroot.getMeasuredWidth();txtView.setLayoutParams(lp);}}private void initVerticalView() {txtView.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence charSequence, int i,int i2, int i3) {}@Overridepublic void onTextChanged(CharSequence charSequence, int i, int i2,int i3) {}@Overridepublic void afterTextChanged(Editable editable) { // Log.d(TAG,"afterTextChanged");final boolean continueAnimation = mStarted;reset();prepareAnimation();cutVerticalTextView();/* * if (continueAnimation) { Log.d(TAG, * "TextChange Restart play"); startMarquee(); } */vroot.post(new Runnable() {@Overridepublic void run() {if (continueAnimation) { // Log.d(TAG, "TextChange Restart play");startMarquee();}}});}});}private void expandVerticalTextView() { // Log.d(TAG, "expandVerticalTextView");ViewGroup.LayoutParams lp = txtView.getLayoutParams();lp.height = TEXTVIEW_HEIGHT;txtView.setLayoutParams(lp);}private void cutVerticalTextView() {// Log.d(TAG, "cutVerticalTextView");if (txtView.getHeight() != vroot.getMeasuredHeight()) {ViewGroup.LayoutParams lp = txtView.getLayoutParams();lp.height = vroot.getMeasuredHeight();txtView.setLayoutParams(lp);}}}

水平滚动布局:text_horizontal_scroll.xml

<?xml version="1.0" encoding="utf-8"?><HorizontalScrollView    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="1000dip"    android:layout_height="match_parent"    android:scrollbars="none">        <TextView       android:id="@+id/textViewHorizontal"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:gravity="left"                android:textSize="30sp"                android:textStyle="bold"                /></HorizontalScrollView>

垂直滚动布局:

<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="1000dip"    android:scrollbars="none">        <TextView            android:id="@+id/textViewVertical"            android:layout_width="wrap_content"            android:layout_height="match_parent"            android:gravity="center_vertical"            android:textSize="35sp"            android:textStyle="bold"            /> </ScrollView>

使用:

private TextMarqueeView textContainer;void textScorllText(){textContainer.setTextContent(content);textContainer.setTextSpeed(TextMarqueeView.SPEED_FAST);textContainer.setCurrentScrollDirection(TextMarqueeView.SCROLL_HORIZONTAL_LEFT);textContainer.startMarquee();}




原创粉丝点击