1. 程式人生 > >Android——TextView實現真正的跑馬燈效果

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(); //開始
    }

}

這樣使用起來就方便了,一起看下其效果:

這裡寫圖片描述