實現(xiàn)方式1
<TextView
android:id="@+id/tvTip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFDE9EB"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:paddingLeft="10dp"
android:paddingTop="12dp"
android:paddingRight="10dp"
android:paddingBottom="12dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:text="物流公告:受疫情影響,2020-08-20起,深圳地區(qū)暫時不支持發(fā)貨"
android:textColor="#FFEB2C3E"
android:textSize="15sp">
</TextView>
//設(shè)置跑馬燈效果芥炭,避免跑馬燈效果失效
private void setMarqueeText() {
mTvTip.setEllipsize(TextUtils.TruncateAt.MARQUEE);
mTvTip.setSingleLine(true);
mTvTip.setSelected(true);
mTvTip.setFocusable(true);
mTvTip.setFocusableInTouchMode(true);
}
跑馬燈相關(guān)屬性
android:singleLine="true" //設(shè)置單行
android:scrollHorizontally="true"
android:ellipsize="marquee" //跑馬燈
android:focusable="true" //獲得焦點
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever" //無限循環(huán)
實現(xiàn)方式2:自定義跑馬燈類
上面方式1能暫時實現(xiàn)跑馬燈效果晴股,但在多次點擊事件之后容易失焦。而且在Android4.4上實現(xiàn)有短暫停頓踊赠。
MarqueeTextView
public class MarqueeTextView extends AppCompatTextView {
/**
* 滾動次數(shù)
*/
private int marqueeNum = -1;//-1為永久循環(huán)呵扛,大于0是循環(huán)次數(shù)。
public void setMarqueeNum(int marqueeNum) {
this.marqueeNum = marqueeNum;
}
public MarqueeTextView(Context context) {
super(context);
setAttr();
}
public MarqueeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setAttr();
}
public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setAttr();
}
/**
* 始終獲取焦點
* 跑馬燈在TextView處于焦點狀態(tài)的時候才會滾動
*/
@Override
public boolean isFocused() {
return true;
}
/**
* 設(shè)置相關(guān)屬性
*/
private void setAttr() {
this.setEllipsize(TextUtils.TruncateAt.MARQUEE);//設(shè)置跑馬等效果
this.setMarqueeRepeatLimit(marqueeNum);//設(shè)置跑馬燈重復(fù)次數(shù)
this.setSingleLine(true);//設(shè)置單行
}
}
<com.zly.demo10.MarqueeTextView2
android:id="@+id/tv_person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#FFFDE9EB"
android:paddingLeft="10dp"
android:paddingTop="12dp"
android:paddingRight="10dp"
android:paddingBottom="12dp"
android:text="物流公告:受疫情影響筐带,2020-08-20起今穿,深圳地區(qū)暫時不支持發(fā)貨"
android:textColor="#FFEB2C3E"
android:textSize="15sp" />
實現(xiàn)方式3:自定義跑馬燈類(自定義繪制控件)
MarqueeTextView
/**
* 自定義跑馬燈
*/
public class MarqueeTextView extends View {
/**
* 界面刷新時間(ms)
*/
public static final int INVALIDATE_TIME = 12;
/**
* 每次移動的像素點(px)
*/
public static final int INVALIDATE_STEP = 2;
/**
* 是否第一次
*/
//private boolean isfirstCycle = true;
private String drawingText;
private TextPaint paint;
public boolean exitFlag;
private float textWidth;
private int posX = 0;
private float posY;
private int width;
private RectF rf;
private boolean hasInit;
private Handler mHandler = new Handler();
public MarqueeTextView(Context context) {
this(context, null);
}
public MarqueeTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context, attrs, defStyle);
}
private void initView(Context context, AttributeSet attrs, int defStyle) {
paint = new TextPaint();
rf = new RectF(0, 0, 0, 0);
paint.setAntiAlias(true);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MarqueeTextView);
setTextSize(typedArray.getDimension(R.styleable.MarqueeTextView_mtvtextsize, 30f));
setTextColor(typedArray.getColor(R.styleable.MarqueeTextView_mtvtextcolor, Color.WHITE));
setText(typedArray.getString(R.styleable.MarqueeTextView_mtvtext));
}
public void setText(String text) {
this.drawingText = text;
posX = 0;
}
public void setTextSize(float dp) {
if (this.paint == null)
return;
this.paint.setTextSize(dp);
}
public void setTextColor(int color) {
if (this.paint == null)
return;
this.paint.setColor(color);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
if (drawingText != null) {
textWidth = paint.measureText(drawingText, 0, drawingText.length());
}
if (posX == 0 && !hasInit) {
rf.right = 0;
rf.bottom = MeasureSpec.getSize(heightMeasureSpec);
posX = 0;
posY = getTextDrawingBaseline(paint, rf);
hasInit = true;
}
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
if (getVisibility() != View.VISIBLE || TextUtils.isEmpty(drawingText)) {
return;
}
canvas.save();
canvas.drawText(drawingText, 0, drawingText.length(), posX, posY, paint);
canvas.restore();
}
private Runnable moveRun = new Runnable() {
@Override
public void run() {
//控制文本寬度大于控件寬度才進(jìn)行滾動
if (width >= textWidth) {
posX = 0;
invalidate();
return;
}
//左移
posX -= INVALIDATE_STEP;
//當(dāng)文字和空格完全移出屏幕,x值從1開始移動
if (posX < -1 * textWidth - paint.measureText("", 0, "".length())) {
posX = getWidth();
}
invalidate();
if (!exitFlag) {
mHandler.postDelayed(this, INVALIDATE_TIME);
return;
}
posX = 0;
}
};
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopMove();
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
if (visibility == View.VISIBLE) {
startMove();
} else {
stopMove();
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
startMove();
} else {
stopMove();
}
}
private void stopMove() {
exitFlag = true;
if (mHandler == null)
return;
mHandler.removeCallbacksAndMessages(null);
}
public void startMove() {
exitFlag = false;
if (mHandler == null)
return;
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(moveRun, 0);
}
public void startMove(long delay) {
exitFlag = false;
if (mHandler == null)
return;
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(moveRun, delay);
}
/**
* 獲取繪制文字的baseline
*
* @param paint
* @param targetRect
* @return
*/
public static float getTextDrawingBaseline(Paint paint, RectF targetRect) {
if (paint == null || targetRect == null) {
return 0;
}
Paint.FontMetrics fontMetric = paint.getFontMetrics();
return targetRect.top + (targetRect.height() - fontMetric.bottom + fontMetric.top) / 2.0f - fontMetric.top;
}
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MarqueeTextView">
<attr name="mtvtextsize" format="dimension"/>
<attr name="mtvtextcolor" format="color"/>
<attr name="mtvtext" format="string"/>
</declare-styleable>
</resources>
使用
<com.zly.demo10.MarqueeTextView
android:id="@+id/tvTip3"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="16dp"
android:background="#FFFDE9EB"
app:mtvtext="物流公告:受疫情影響伦籍,2020-08-20起蓝晒,深圳地區(qū)暫時不支持發(fā)貨"
app:mtvtextcolor="#FFEB2C3E"
app:mtvtextsize="15sp"
tools:text="物流公告:受疫情影響腮出,2020-08-20起,深圳地區(qū)暫時不支持發(fā)貨" />
mTvTip3.setText("倉為品牌特賣倉儲中心芝薇,庫存較少利诺,請盡a電視劇花短時間撒分散打發(fā)第三方大師傅大師傅奧德賽飛a");
mTvTip3.setTextColor(Color.BLACK);
mTvTip3.startMove(1000);
第三方跑馬燈庫
MarqueeView:可垂直跑、可水平跑的跑馬燈剩燥。
MarqueeViewLibrary:一個很方便使用和擴(kuò)展的跑馬燈Library慢逾,通過提供不同的MarqueeFactory來定制不同的跑馬燈View, 并且提供了常用類型的跑馬燈效果:SimpleMarqueeView灭红。
第三方跑馬燈庫MarqueeView的使用
implementation 'com.sunfusheng:MarqueeView:1.4.1'
XML
<com.sunfusheng.marqueeview.MarqueeView
android:id="@+id/marqueeView"
android:layout_width="match_parent"
android:layout_height="30dp"
app:mvAnimDuration="1000"
app:mvDirection="bottom_to_top"
app:mvInterval="3000"
app:mvTextColor="@color/white"
app:mvTextSize="14sp"
app:mvSingleLine="true"
app:mvFont="@font/huawenxinwei"/>
設(shè)置字符串列表數(shù)據(jù)侣滩,或者設(shè)置自定義的Model數(shù)據(jù)類型
MarqueeView marqueeView = (MarqueeView) findViewById(R.id.marqueeView);
List<String> messages = new ArrayList<>();
messages.add("1. 大家好,我是孫福生变擒。");
messages.add("2. 歡迎大家關(guān)注我哦君珠!");
messages.add("3. GitHub帳號:sunfusheng");
messages.add("4. 新浪微博:孫福生微博");
messages.add("5. 個人博客:sunfusheng.com");
messages.add("6. 微信公眾號:孫福生");
marqueeView.startWithList(messages);
// 或者設(shè)置自定義的Model數(shù)據(jù)類型
public class CustomModel implements IMarqueeItem {
@Override
public CharSequence marqueeMessage() {
return "...";
}
}
List<CustomModel> messages = new ArrayList<>();
marqueeView.startWithList(messages);
// 在代碼里設(shè)置自己的動畫
marqueeView.startWithList(messages, R.anim.anim_bottom_in, R.anim.anim_top_out);
設(shè)置字符串?dāng)?shù)據(jù)
String message = "心中有陽光,腳底有力量娇斑!心中有陽光策添,腳底有力量!心中有陽光毫缆,腳底有力量唯竹!";
marqueeView.startWithText(message);
// 在代碼里設(shè)置自己的動畫
marqueeView.startWithText(message, R.anim.anim_bottom_in, R.anim.anim_top_out);
設(shè)置事件監(jiān)聽
marqueeView.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
@Override
public void onItemClick(int position, TextView textView) {
Toast.makeText(getApplicationContext(), String.valueOf(marqueeView1.getPosition()) + ". " + textView.getText(), Toast.LENGTH_SHORT).show();
}
});
在 Activity 或 Fragment 中
@Override
public void onStart() {
super.onStart();
marqueeView.startFlipping();
}
@Override
public void onStop() {
super.onStop();
marqueeView.stopFlipping();
}
在 ListView 或 RecyclerView 的 Adapter 中
@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.marqueeView.stopFlipping();
}