创建自定义控件VerticalRollingTextView
public class VerticalRollingTextView extends View {
DataSetAdapter mDataSetAdapter;
private final Paint mPaint;
private int mCurrentIndex;
private int mNextIndex;
Rect bounds = new Rect();
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 = 2000;
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() {
mCurrentIndex++;
mCurrentIndex = mCurrentIndex < mDataSetAdapter.getItemCount() ? mCurrentIndex : mCurrentIndex % mDataSetAdapter.getItemCount();
confirmNextIndex();
mCurrentOffsetY = mOrgOffsetY;
mAnimationEnded = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallbacks(mRollingTask);
if (isRunning()) {
mAnimation.cancel();
}
}
/**
* 计算第二个角标
*/
private void confirmNextIndex() {
mNextIndex = mCurrentIndex + 1;
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) {
animationEnd();
}
postInvalidate();
}
public void updateValue(float startValue, float endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
}
}
然后在values文件夹下创建attrs,自定义属性
<resources>
<declare-styleable name="VerticalRollingTextView">
<attr name="android:textColor"/>
<attr name="android:textSize"/>
<attr name="android:duration"/>
<attr name="animInterval" format="integer"/>
</declare-styleable>
</resources>
最后在布局中调用
<com.bawei.redchild.view.VerticalRollingTextView
android:id="@+id/home_rolling_tv"
android:layout_height="40dp"
android:layout_width="match_parent"
android:layout_alignParentLeft="true"
myattrs:animInterval="3000"
android:layout_marginLeft="75dp"
android:layout_marginTop="8dp"
android:textSize="18sp"
/>
在代码中获取控件,并给控件设置数据
mVerticalRollingView.setDataSetAdapter(new DataSetAdapter<String>(Arrays.asList(mStrs)) {
@Override
protected String text(String s) {
return s;
}
});
可以设置点击监听
mVerticalRollingView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(VerticalRollingTextView view, int index) {
Toast.makeText(MainActivity.this, mStrs[index], Toast.LENGTH_SHORT).show();
}
});
最后开始,或者停止这个滚动字幕
if (mVerticalRollingView.isRunning()) {
mVerticalRollingView.stop();
button.setText("滚动");
} else {
mVerticalRollingView.run();
button.setText("停止");
}