Android 上下滚动字幕实现

private float mCurrentOffsetY;



private float mOrgOffsetY = -1;



private final float mTextTopToAscentOffset;

private float mOffset;



private InternalAnimation mAnimation = new InternalAnimation();



/*防止动画结束的回调触发以后动画继续进行出现的错乱问题*/

private boolean mAnimationEnded;



private boolean isRunning;

/*动画时间*/

private int mDuration = 1000;

/*动画间隔*/

private int mAnimInterval = 5000;



public VerticalRollingTextView(Context context, AttributeSet attrs) {

    super(context, attrs);

    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    mPaint.setColor(Color.BLACK);

    mPaint.setTypeface(Typeface.DEFAULT);



    parseAttrs(context, attrs);



    Paint.FontMetricsInt metricsInt = mPaint.getFontMetricsInt();

    mTextTopToAscentOffset = metricsInt.ascent - metricsInt.top;



    mAnimation.setDuration(mDuration);

}



private void parseAttrs(Context context, AttributeSet attrs) {

    float density = getResources().getDisplayMetrics().density;

    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VerticalRollingTextView);

    mPaint.setColor(typedArray.getColor(R.styleable.VerticalRollingTextView_android_textColor, Color.BLACK));

    mPaint.setTextSize(typedArray.getDimensionPixelOffset(R.styleable.VerticalRollingTextView_android_textSize, (int) (density * 14)));

    mDuration = typedArray.getInt(R.styleable.VerticalRollingTextView_android_duration, mDuration);

    mAnimInterval = typedArray.getInt(R.styleable.VerticalRollingTextView_animInterval, mAnimInterval);



    typedArray.recycle();

}



@Override

protected void onDraw(Canvas canvas) {

    // 绘制文本

    if (mDataSetAdapter == null || mDataSetAdapter.isEmpty()) {

        return;

    }



    String text1 = mDataSetAdapter.getText(mCurrentIndex);

    String text2 = mDataSetAdapter.getText(mNextIndex);



    //只需要进行一次测量

    if (mOrgOffsetY == -1) {

        mPaint.getTextBounds(text1, 0, text1.length(), bounds);

        mOffset = (getHeight() + bounds.height()) * 0.5f;

        mOrgOffsetY = mCurrentOffsetY = mOffset - mTextTopToAscentOffset;

        mAnimation.updateValue(mOrgOffsetY, -2 * mTextTopToAscentOffset);

    }



    canvas.drawText(text1, 0, mCurrentOffsetY, mPaint);

    canvas.drawText(text2, 0, mCurrentOffsetY + mOffset + mTextTopToAscentOffset, mPaint);

}



public void setDataSetAdapter(DataSetAdapter dataSetAdapter) {

    mDataSetAdapter = dataSetAdapter;

    confirmNextIndex();

    invalidate();

}



/**

 * 开始转动,界面可见的时候调用

 */

public void run() {

    if (isRunning) {

        return;

    }



    isRunning = true;

    mAnimation.updateValue(mCurrentOffsetY, -2 * mTextTopToAscentOffset);

    post(mRollingTask);

}



/**

 * @return true代表正在转动

 */

public boolean isRunning() {

    return isRunning;

}



/**

 * 停止转动,界面不可见的时候调用

 */

public void stop() {

    isRunning = false;

    removeCallbacks(mRollingTask);

}



Runnable mRollingTask = new Runnable() {

    @Override

    public void run() {

        mAnimationEnded = false;

        startAnimation(mAnimation);

        postDelayed(this, mAnimInterval);

    }

};





public void animationEnd() {

    //1.角标+1

    mCurrentIndex++;

    //2.计算出正确的角标

    mCurrentIndex = mCurrentIndex < mDataSetAdapter.getItemCount() ? mCurrentIndex : mCurrentIndex % mDataSetAdapter.getItemCount();

    //3.计算下一个待显示文字角标

    confirmNextIndex();

    //3.位置复位

    mCurrentOffsetY = mOrgOffsetY;

    mAnimationEnded = true;

}



@Override

protected void onDetachedFromWindow() {

    super.onDetachedFromWindow();

    removeCallbacks(mRollingTask);

    if (isRunning()) {

        mAnimation.cancel();

    }

}



/**

 * 计算第二个角标

 */

private void confirmNextIndex() {

    //3.计算第二个角标

    mNextIndex = mCurrentIndex + 1;

    //4.计算出正确的第二个角标

    mNextIndex = mNextIndex < mDataSetAdapter.getItemCount() ? mNextIndex : 0;

}



/**

 * float估值器

 *

 * @param fraction

 * @param startValue

 * @param endValue

 * @return

 */

float evaluate(float fraction, float startValue, float endValue) {

    return startValue + fraction * (endValue - startValue);

}



@Override

public void setOnClickListener(OnClickListener l) {



}



public void setOnItemClickListener(final OnItemClickListener onItemClickListener) {

    super.setOnClickListener(new OnClickListener() {

        @Override

        public void onClick(View v) {

            onItemClickListener.onItemClick(VerticalRollingTextView.this, mCurrentIndex);

        }

    });

}



public interface OnItemClickListener {

    void onItemClick(VerticalRollingTextView view, int index);

}



class InternalAnimation extends Animation {



    float startValue;

    float endValue;



    @Override

    protected void applyTransformation(float interpolatedTime, Transformation t) {

        if (mAnimationEnded) return;



        mCurrentOffsetY = evaluate(interpolatedTime, startValue, endValue);

        if (mCurrentOffsetY == endValue) {

            try {

                animationEnd();

            }catch (Exception e) {

                e.printStackTrace();

            }



        }

        postInvalidate();

    }



    public void updateValue(float startValue, float endValue) {

        this.startValue = startValue;

        this.endValue = endValue;

    }



}

}




### 2、layout.xml文件



<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值