android 文字实现跑马灯效果,网上也有很多说明,此处做个人记录。
网上有很多方式实现,在这总结一下自己实现和其他大佬实现方式。
方式一:
- 自定义MarqueeTextView 继承自TextView,重写isFocused()方法
public class MarqueeTextView extends TextView {
public MarqueeTextView(Context context) {
super(context);
}
public MarqueeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MarqueeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isFocused() {
//必须重写,且返回值是true,表示始终获取焦点
return true;
}
}
-
在XML文件中添加对应属性设置
主要属性:
android:ellipsize=“marquee” //设置超出显示区域的内容以跑马灯效果呈现,该值还可设置成 END,START 等,就是我们常见的末尾 “…” 显示
android:focusable=“true” //获取焦点
android:focusableInTouchMode=“true”
android:marqueeRepeatLimit=“marquee_forever” //设置循环次数,-1,marquee_forever 表示无线循环
android:singleLine=“true” //设置单行显示参数说明: android:ellipsize = "end" 省略号在结尾 android:ellipsize = "start" 省略号在开头 android:ellipsize = "middle" 省略号在中间 android:ellipsize = "marquee" 跑马灯
在这贴出我使用的,可能有一些不需要,根据需求参考:
<com.washroom.view.MarqueeTextView
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_bulletin_board"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:text="公告:1、我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView,我是一个TextView"
android:textColor="@color/color_cewei_title"
android:textSize="20sp"
android:padding="3dp"
tools:ignore="MissingDefaultResource" />
- 文字长度要比 MarqueeTextView长,
方式二:
该方式是贴出其他大佬使用的方法:
原文链接
该方式已经尝试,可以实现横向文字跑马灯效果且效果较顺滑,如有问题,请在原链接中查找解决方法。
Gradle集成使用:
implementation 'anylife.scrolltextview:ScrollTextviewLib:1.x,y'
Maven集成使用:
<dependency>
<groupId>anylife.scrolltextview</groupId>
<artifactId>ScrollTextviewLib</artifactId>
<version>1.x.y</version>
<type>pom</type>
</dependency>
使用:
<anylife.scrolltextview.ScrollTextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/imageView2"
android:layout_marginRight="6dp"
android:singleLine="true"
customAttr:clickEnable="true"
customAttr:isHorizontal="true"
customAttr:speed="1"
customAttr:text="ScrollTextView Auto Scroll.1234567890,"
customAttr:text_size="14sp"
customAttr:text_color="#ffffffff"
customAttr:times="567" />
或者在代码中:
scrollText = findViewById(R.id.scrollText);
scrollText.setSpeed(4);
scrollText.setText("new text");
scrollText.setTextColor(0xffad43ae);
方式三:
原文链接
此方式可以放置多种view,imageview,textview等
该方式已经测试,可以实现文字跑马灯效果,但是唯一不足,跑马灯有卡顿现象,我在模拟器上运行的,也可能是模拟器问题,根据实际情况参考。
主要实现方式是继承HorizontalScrollView并实现Runnable接口,通过scrollTo方法刷新界面实现。以下是MarqueeView源码:
package xyy.marqueeview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
/**
* 跑马灯View
* Created by xuyy on 2016/8/23.
*/
public class MarqueeView extends HorizontalScrollView implements Runnable{
private Context context;
private LinearLayout mainLayout;//跑马灯滚动部分
private int scrollSpeed = 5;//滚动速度
private int scrollDirection = LEFT_TO_RIGHT;//滚动方向
private int currentX;//当前x坐标
private int viewMargin = 20;//View间距
private int viewWidth;//View总宽度
private int screenWidth;//屏幕宽度
public static final int LEFT_TO_RIGHT = 1;
public static final int RIGHT_TO_LEFT = 2;
public MarqueeView(Context context) {
this(context, null);
}
public MarqueeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MarqueeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
void initView() {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
screenWidth = wm.getDefaultDisplay().getWidth();
mainLayout = (LinearLayout)LayoutInflater.from(context).inflate(R.layout.scroll_content, null);
this.addView(mainLayout);
}
public void addViewInQueue(View view){
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(viewMargin, 0, 0, 0);
view.setLayoutParams(lp);
mainLayout.addView(view);
view.measure(0, 0);//测量view
viewWidth = viewWidth + view.getMeasuredWidth() + viewMargin;
}
/**
* 移除 view
* @param view
* 此方法是通过评论区大佬添加,暂未亲测
*/
public void removeViewInQueue(View view){
mainLayout.removeView(view);
}
//开始滚动
public void startScroll(){
removeCallbacks(this);
currentX = (scrollDirection == LEFT_TO_RIGHT ? viewWidth : -screenWidth);
post(this);
}
//停止滚动
public void stopScroll(){
removeCallbacks(this);
}
//设置View间距
public void setViewMargin(int viewMargin){
this.viewMargin = viewMargin;
}
//设置滚动速度
public void setScrollSpeed(int scrollSpeed){
this.scrollSpeed = scrollSpeed;
}
//设置滚动方向 默认从左向右
public void setScrollDirection(int scrollDirection){
this.scrollDirection = scrollDirection;
}
@Override
public void run() {
switch (scrollDirection){
case LEFT_TO_RIGHT:
mainLayout.scrollTo(currentX, 0);
currentX --;
if (-currentX >= screenWidth) {
mainLayout.scrollTo(viewWidth, 0);
currentX = viewWidth;
}
break;
case RIGHT_TO_LEFT:
mainLayout.scrollTo(currentX, 0);
currentX ++;
if (currentX >= viewWidth) {
mainLayout.scrollTo(-screenWidth, 0);
currentX = -screenWidth;
}
break;
default:
break;
}
postDelayed(this, 50 / scrollSpeed);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
}
addViewInQueue()方法中先将添加的View加到滚动部分mainLayout中,再计算添加View的总宽度,便于下面计算滚动起点。
startScroll()方法中,如果方向是从左向右,那就将滚动起点设为(viewWidth, 0),如果是从右向左,那就将滚动起点设为(-screenWidth, 0)。这里要注意的是scrollTo方法参数为正时在坐标系中是负向(向左)的,参数为负时在坐标系中是正向(向右)的。
mainLayout就是滚动部分了。当mainLayout整体全部滚动出屏幕,即滚动距离=screenWidth+viewWidth时重置起点,实现循环跑马灯的效果。
mainLayout的布局很简单,就是一个横向的LinearLayout。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
使用:
public class MainActivity extends Activity {
private MarqueeView marqueeView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
marqueeView = (MarqueeView) findViewById(R.id.marquee_view);
initMarqueeView();
}
private void initMarqueeView(){
ImageView iv1 = new ImageView(this);
iv1.setImageResource(R.drawable.pic1);
marqueeView.addViewInQueue(iv1);
ImageView iv2 = new ImageView(this);
iv2.setImageResource(R.drawable.pic2);
marqueeView.addViewInQueue(iv2);
ImageView iv3 = new ImageView(this);
iv3.setImageResource(R.drawable.pic3);
marqueeView.addViewInQueue(iv3);
ImageView iv4 = new ImageView(this);
iv4.setImageResource(R.drawable.pic4);
marqueeView.addViewInQueue(iv4);
ImageView iv5 = new ImageView(this);
iv5.setImageResource(R.drawable.pic5);
marqueeView.addViewInQueue(iv5);
ImageView iv6 = new ImageView(this);
iv6.setImageResource(R.drawable.pic6);
marqueeView.addViewInQueue(iv6);
marqueeView.setScrollSpeed(8);
marqueeView.setScrollDirection(MarqueeView.RIGHT_TO_LEFT);
marqueeView.setViewMargin(15);
marqueeView.startScroll();
}
}
MainActivity布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<xyy.marqueeview.MarqueeView
android:id="@+id/marquee_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@color/black60"
android:fillViewport="true"
android:gravity="center"
android:padding="5dp"/>
</RelativeLayout>
其实总结这些,主要原因是公司项目使用的是方法一,在我使用的时候,在部分版本低的模拟器中,text文字移动的时候有卡顿现象,找了一圈没有招到有用的信息。看到一个大佬 的代码需要更改 TextView 源码,小白一个,改了后不知道怎么使用,先贴出来吧,万一后面会用了,又找不到了。
前段时间在维护Android系统的时候,遇到TextView,一旦添加了跑马灯后,UI就出现卡顿的情况。针对这个情况,对Android TextView的源码进行了一系列的分析和测试,最终找到了原因,如下:
在构造函数里面的一段代码
修改前:
public TextView(Context context, AttributeSet attrs, int defStyle) {
......
switch (ellipsize) {
case 1:
setEllipsize(TextUtils.TruncateAt.START);
break;
case 2:
setEllipsize(TextUtils.TruncateAt.MIDDLE);
break;
case 3:
setEllipsize(TextUtils.TruncateAt.END);
break;
case 4:
if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
setHorizontalFadingEdgeEnabled(true);
mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
} else {
setHorizontalFadingEdgeEnabled(false);
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
}
setEllipsize(TextUtils.TruncateAt.MARQUEE);
break;
}
......
}
修改后:
public TextView(Context context, AttributeSet attrs, int defStyle) {
......
switch (ellipsize) {
case 1:
setEllipsize(TextUtils.TruncateAt.START);
break;
case 2:
setEllipsize(TextUtils.TruncateAt.MIDDLE);
break;
case 3:
setEllipsize(TextUtils.TruncateAt.END);
break;
case 4:
/*if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
setHorizontalFadingEdgeEnabled(true);
mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
} else {
setHorizontalFadingEdgeEnabled(false);
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
}*/
setEllipsize(TextUtils.TruncateAt.MARQUEE);
break;
}
......
}
总结:
注释掉的地方的功能是TextView左右的阴影部分,注释掉后,跑马灯左右没有阴影效果,所以也不算是根治此问题,只能够说变相的优化了。但是我对比过要不要阴影的效果,不特别去注意的话是看不出有什么不同的。Android4.4绘制UI感觉是有些问问题,在Server使用WindowManager画UI的时候,明显比Android5.0以上卡很多,具体原因没有过多去跟了,有继续往下跟的大神,找到原因了的话,可以分享一下,感谢!