Android——TextView实现真正的跑马灯效果

  android 应用有时候需要做一个广告栏,可以做文字闪烁,跑马灯等效果,文字闪烁可以放在线程里面执行,多少秒过后设置文字的TextColor即可,跑马灯效果很少接触,之前写了一个demo,但是实现的效果不是自己想要的,普遍的跑马灯效果直接在layout里面的TextView控件加上如下属性即可实现:
  

android:marqueeRepeatLimit="marquee_forever"
android:ellipsize="marquee" 
android:scrollHorizontally="true"
android:focusableInTouchMode="true"
android:focusable="true"

但是这种效果有一个致命的问题,文字的宽度必须要大于TextView设置的宽度(android:layout_width=”“),有时候需求文字很少,甚至根本没法实现跑马灯效果,这是让人头疼的事。

当然肯定有解决的办法,第一想到的就是自定义,急需要用的时候,自定义显得太麻烦了,耗时间,这里给大家介绍一个封装好的jar,下面一起来实现这个效果。

先要加入marquee的jar包,在项目的build.gradle文件中加入

compile 'com.dalong:marqueeview:1.0.0'

这里面只有一个类,自定义的MarqueeView,简单的来看一下:

package com.dalong.marqueeview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

/**
 * Created  16/5/15.
 */
public class MarqueeView  extends SurfaceView implements SurfaceHolder.Callback{
    public Context  mContext;

    private float mTextSize = 100; //字体大小

    private int mTextColor = Color.RED; //字体的颜色

    private int mBackgroundColor=Color.WHITE;//背景色

    private boolean mIsRepeat;//是否重复滚动

    private int mStartPoint;// 开始滚动的位置  0是从最左面开始    1是从最末尾开始

    private int mDirection;//滚动方向 0 向左滚动   1向右滚动

    private int mSpeed;//滚动速度

    private SurfaceHolder holder;

    private TextPaint mTextPaint;

    private MarqueeViewThread mThread;

    private String margueeString;

    private int textWidth=0,textHeight=0;

    private int ShadowColor=Color.BLACK;

    public int currentX=0;// 当前x的位置

    public int sepX=5;//每一步滚动的距离

    public MarqueeView(Context context) {
        this(context,null);
    }

    public MarqueeView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MarqueeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext=context;
        init(attrs, defStyleAttr);
    }

    private void init(AttributeSet attrs, int defStyleAttr) {

        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MarqueeView, defStyleAttr, 0);
        mTextColor = a.getColor(R.styleable.MarqueeView_textcolor, Color.RED);
        mTextSize = a.getDimension(R.styleable.MarqueeView_textSize, 48);
        mBackgroundColor=a.getColor(R.styleable.MarqueeView_marqueebackground,Color.BLACK);
        mIsRepeat=a.getBoolean(R.styleable.MarqueeView_isRepeat,false);
        mStartPoint=a.getInt(R.styleable.MarqueeView_startPoint,0);
        mDirection=a.getInt(R.styleable.MarqueeView_direction,0);
        mSpeed=a.getInt(R.styleable.MarqueeView_speed,20);
        a.recycle();

        holder = this.getHolder();
        holder.addCallback(this);
        mTextPaint = new TextPaint();
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Paint.Align.LEFT);
    }

    public void setText(String msg){
        if(!TextUtils.isEmpty(msg)){
            measurementsText(msg);
        }
    }
    protected void measurementsText(String msg) {
        margueeString=msg;
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setStrokeWidth(0.5f);
        mTextPaint.setFakeBoldText(true);
        // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色)
//        mTextPaint.setShadowLayer(5, 3, 3, ShadowColor);
        textWidth = (int)mTextPaint.measureText(margueeString);
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        textHeight = (int) fontMetrics.bottom;
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();
        if(mStartPoint==0)
            currentX=0;
        else
            currentX=width-getPaddingLeft()-getPaddingRight();

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        this.holder=holder;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if(mThread!=null)
            mThread.isRun = true;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if(mThread!=null)
            mThread.isRun = false;
    }

    /**
     * 开始滚动
     */
    public  void startScroll(){

        if(mThread!=null&&mThread.isRun)
            return;
        mThread = new MarqueeViewThread(holder);//创建一个绘图线程
        mThread.start();
    }

    /**
     * 停止滚动
     */
    public  void stopScroll(){
        if(mThread!=null){
            mThread.isRun = false;
            mThread.interrupt();
        }
        mThread=null;
    }
    /**
     * 线程
     */
    class MarqueeViewThread extends Thread{

        private SurfaceHolder holder;

        public boolean isRun ;//是否在运行


        public  MarqueeViewThread(SurfaceHolder holder) {
            this.holder =holder;
            isRun = true;
        }

        public void onDraw() {
            try {
                synchronized (holder) {
                    if (TextUtils.isEmpty(margueeString)) {
                        Thread.sleep(1000);//睡眠时间为1秒
                        return;
                    }
                    Canvas canvas = holder.lockCanvas();
                    int paddingLeft = getPaddingLeft();
                    int paddingTop = getPaddingTop();
                    int paddingRight = getPaddingRight();
                    int paddingBottom = getPaddingBottom();

                    int contentWidth = getWidth() - paddingLeft - paddingRight;
                    int contentHeight = getHeight() - paddingTop - paddingBottom;

                    int centeYLine = paddingTop + contentHeight / 2;//中心线

                    if(mDirection==0) {//向左滚动
                        if(currentX <=-textWidth){
                            if(!mIsRepeat){//如果是不重复滚动
                                mHandler.sendEmptyMessage(ROLL_OVER);
                            }
                            currentX=contentWidth;
                        }else{
                            currentX-=sepX;
                        }
                    }else {//  向右滚动
                        if(currentX>=contentWidth){
                            if(!mIsRepeat){//如果是不重复滚动
                                mHandler.sendEmptyMessage(ROLL_OVER);
                            }
                            currentX=-textWidth;
                        }else{
                            currentX+=sepX;
                        }
                    }

                    if(canvas!=null)
                        canvas.drawColor(mBackgroundColor);
                    canvas.drawText(margueeString,currentX, centeYLine+dip2px(getContext(),textHeight)/2,mTextPaint);
                    holder.unlockCanvasAndPost(canvas);//结束锁定画图,并提交改变。

                    int a=textWidth/margueeString.trim().length();
                    int b=a/sepX;
                    int c=mSpeed/b==0?1:mSpeed/b;

                    Thread.sleep(c);//睡眠时间为移动的频率


                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }

        }

        @Override
        public void run() {
            while (isRun) {
                onDraw();
            }

        }

    }

    public static  final  int  ROLL_OVER =100;
    Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what){
                case ROLL_OVER:
                    stopScroll();
                    if(mOnMargueeListener!=null){
                        mOnMargueeListener.onRollOver();
                    }
                    break;
            }
        }
    };

    /**
     * dip转换为px
     * @param context
     * @param dpValue
     * @return
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public void reset(){
        int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        if(mStartPoint==0)
            currentX=0;
        else
            currentX=contentWidth;
    }
    /**
     * 滚动回调
     */
    public interface OnMargueeListener{
        void onRollOver();//滚动完毕
    }

    OnMargueeListener mOnMargueeListener;

    public void setOnMargueeListener(OnMargueeListener mOnMargueeListener){
        this.mOnMargueeListener=mOnMargueeListener;
    }
}

以上代码写明了注释,就不多说了,大家可以了解下。

下面先写一个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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <com.dalong.marqueeview.MarqueeView
            android:id="@+id/tv_marquee"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:direction="left"
            app:isRepeat="true"
            app:speed="50"
            app:startPoint="end"
            app:textSize="12sp"
            app:textcolor="#E72803" />

    </LinearLayout>

</LinearLayout>

只加了一个自定义控件,app:direction=”left” 表示向左滚动,根据自己的需求设定方向;app:isRepeat=”true” 是否重复滚动;app:speed=”100” 设置滚动的速度,值越小速度越快,值越大速度越慢,app:startPoint=”end” 是文字的起始点。

经亲自测试:该自定义控件只能再layout里面使用一次,不可重复使用多个。

然后再MainActivity里面设置开始滚动就OK了,还是把代码贴出来吧!

package com.lai.marqueedemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.dalong.marqueeview.MarqueeView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MarqueeView marqueeView = (MarqueeView)findViewById(R.id.tv_marquee);
        marqueeView.setFocusable(true);
        marqueeView.requestFocus();
        marqueeView.setText("我使劲跑");//设置文本
        marqueeView.startScroll(); //开始
    }

}

这样使用起来就方便了,一起看下其效果:

这里写图片描述

源码下载地址

俗名:垂直跑马灯;学名:垂直翻页公告使用Gradle:compile 'com.sunfusheng:marqueeview:1.1.0'属性Attribute 属性Description 描述mvAnimDuration一行文字动画执行时间mvInterval两行文字翻页时间间隔mvTextSize文字大小mvTextColor文字颜色mvGravity文字位置:left、center、rightmvSingleLine单行设置xml<com.sunfusheng.marqueeview.MarqueeView     android:id="@ id/marqueeView"     android:layout_width="match_parent"     android:layout_height="30dp"     app:mvAnimDuration="1000"     app:mvInterval="3000"     app:mvTextColor="@color/white"     app:mvTextSize="14sp"     app:mvSingleLine="true"/>设置列表数据MarqueeView marqueeView = (MarqueeView) findViewById(R.id.marqueeView); List<String> info = new ArrayList<>(); info.add("1. 大家好,我是孙福生。"); info.add("2. 欢迎大家关注我哦!"); info.add("3. GitHub帐号:sfsheng0322"); info.add("4. 新浪微博:孙福生微博"); info.add("5. 个人博客:sunfusheng.com"); info.add("6. 微信公众号:孙福生"); marqueeView.startWithList(info);设置字符串数据String notice = "心中有阳光,脚底有力量!心中有阳光,脚底有力量!心中有阳光,脚底有力量!"; marqueeView.startWithText(notice);设置事件监听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();     } });
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值