android自定义View实现跑马灯效果

android自带的TextView可以实现跑马灯效果,但是有很多的局限性;比如需要设置ellipsize=”marquee”,获取 focusable=”true”,设置singleLine=”true”,控件里的内容需要超过控件本身的长度,无法控制滚动速度和滚动暂停和继续滚动功能,各种限制导致用起来特别不顺手,几乎无法使用到生产环境中,在此背景下,需要自定义View实现跑马灯效果。

使用主要方法:自定义View重写onDraw方法,通过canvas.drawText()方法来显示文字,利用Handler不断地绘制文字,并控制文字开始绘制的X轴的位置,来实现连续滚动的效果。

实现步骤:

1、自定义view的class:

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class MarqueenView extends View {

 /*
  * 1、自定义View的属性 2、在View的构造方法中获得我们自定义的属性 [ 3、重写onMesure ] 4、重写onDraw
  */

 /*
  * 文本
  */
 private String mTitleText;

 /**
  * 文本的颜色
  */
 private int mTitleTextColor;

 /**
  * 文本的大小
  */
 private int mTitleTextSize;

 /**
  * 绘制时控制文本绘制的范围
  */
 private Rect mBound, usualBound;

 // 画笔
 private Paint mPaint;

 private int spead = 15;

 private int length = 3;

 private int currentLength;

 @SuppressLint("HandlerLeak")
 private Handler handler = new Handler() {
  @Override
  public void handleMessage(android.os.Message msg) {
   if (mBound.width() <= getWidth()) {
    if (currentLength >= 120 + (getWidth() / length) * length) {
     currentLength = length;
    } else {
     currentLength = currentLength + length;
    }
   } else {
    if (currentLength >= (mBound.width() + 120)) {
     currentLength = length;
    } else {
     currentLength = currentLength + length;
    }
   }

   invalidate();
   handler.sendEmptyMessageDelayed(0, spead);

  };
 };

 public MarqueenView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
  currentLength = length;
 }

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

 public MarqueenView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);

  TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);

  int n = a.getIndexCount();
  for (int i = 0; i < n; i++) {
   int attr = a.getIndex(i);

   switch (attr) {
   case R.styleable.CustomTitleView_titleText:
    mTitleText = a.getString(attr);
    break;
   case R.styleable.CustomTitleView_titleTextColor:
    mTitleTextColor = a.getColor(attr, Color.BLACK);
    break;
   case R.styleable.CustomTitleView_titleTextSize:
    mTitleTextSize = a.getDimensionPixelSize(attr,
      (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
    break;
   }

  }
  a.recycle();

  mPaint = new Paint();
  mPaint.setTextSize(mTitleTextSize);

  mBound = new Rect();
  usualBound = new Rect();
  mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
  mPaint.getTextBounds("1234567890QqYy你好", 0, 16, usualBound);
 }

 /*
  * EXACTLY:一般是设置了明确的值或者是MATCH_PARENT AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
  * UNSPECIFIED:表示子布局想要多大就多大,很少使用
  */

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);

  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  int width;
  int height;
  if (widthMode == MeasureSpec.EXACTLY) {
   width = widthSize;
  } else {
   mPaint.setTextSize(mTitleTextSize);
   mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
   float textWidth = mBound.width();// 字体宽度

   // 控件padding
   int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
   width = desired;
  }

  if (heightMode == MeasureSpec.EXACTLY) {
   height = heightSize;
  } else {
   mPaint.setTextSize(mTitleTextSize);
   mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
   float textHeight = mBound.height();
   int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
   height = desired;
  }
  // setMeasuredDimension((width / length) * length, height);
  setMeasuredDimension(width, height + usualBound.bottom);
 }

 @Override
 protected void onDraw(Canvas canvas) {

  mPaint.setColor(mTitleTextColor);
  if (mBound.width() <= getWidth()) {

   if (currentLength < mBound.width()) {
    canvas.drawText(mTitleText, getPaddingLeft() - currentLength, getHeight() - usualBound.bottom, mPaint);
   }

   if (currentLength >= 120) {
    canvas.drawText(mTitleText, getWidth() - currentLength + 120, getHeight() - usualBound.bottom, mPaint);
   }

  } else {

   if (currentLength < mBound.width()) {
    canvas.drawText(mTitleText, getPaddingLeft() - currentLength, getHeight() - usualBound.bottom, mPaint);
   }

   if (currentLength >= mBound.width() - getWidth() + 120) {
    canvas.drawText(mTitleText, 120 - currentLength + mBound.width(), getHeight() - usualBound.bottom, mPaint);
   }

  }

 }

 public static int getFontHeight(Integer textSize) {
  Paint paint = new Paint();
  if (textSize != null) {
   paint.setTextSize(textSize);
  }
  FontMetrics fm = paint.getFontMetrics();
  return (int) (fm.descent - fm.ascent);
 }

 public void startScroll() {
  handler.removeMessages(0);
  handler.sendEmptyMessage(0);
 }

 public void stopScroll() {
  handler.removeMessages(0);
  currentLength = 0;
  invalidate();
 }

 public void setScrollLength(int length) {
  this.length = length;
  currentLength = length;
 }

 public void setSpead(int spead) {
  this.spead = spead;
 }

 public void setText(String msg) {
  mTitleText = msg;
  mBound = new Rect();
  mPaint.getTextBounds(msg, 0, mTitleText.length(), mBound);
 }

}

2、在values的attrs.xml文件中需要加入自定义的属性,字体大小、颜色、和初始内容,这里有待改进

<?xml version="1.0" encoding="utf-8"?>
<resources>

 <attr name="titleText" format="string" />
 <attr name="titleTextColor" format="color" />
 <attr name="titleTextSize" format="dimension" />

 <declare-styleable name="CustomTitleView">
  <attr name="titleText" />
  <attr name="titleTextColor" />
  <attr name="titleTextSize" />
 </declare-styleable>
</resources>

3、在layout的布局文件中引用此控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:custom="http://schemas.android.com/apk/res/***这里写上自己的包名***"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

 <com.example.MarqueenView
 android:id="@+id/main_marquee_view"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 custom:titleText=" "
 custom:titleTextColor="#666666"
 custom:titleTextSize="14sp"/>
 </LinearLayout>

4、上面准备好后,就可以在activity中设置跑马灯并开始滚动了

mv = (MarqueenView) findViewById(R.id.main_marquee_view);
mv.stopScroll();
mv.setText("滚动内容");// 设置显示内容
//mv.setSpead(15);//滚动频率,默认15毫秒一次,数值太小会影响效率
//mv.setScrollLength(3);//默认每次左移3px,数值太大会有停顿感,数值太小滚动会变慢
mv.startScroll();

//记得在Ondestroy中停止滚动,方便回收
@Override
 protected void onDestroy() {
  try {
   mv.stopScroll();
   mv = null;
  } catch (Throwable e) {
   e.printStackTrace();
  }
  super.onDestroy();
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义textview实现竖直滚动跑马灯效果

    本文实例为大家分享了Android自定义textview实现跑马灯效果的具体代码,供大家参考,具体内容如下 xml布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.c

  • Android基于TextView不获取焦点实现跑马灯效果

    本文实例讲述了Android基于TextView不获取焦点实现跑马灯效果.分享给大家供大家参考,具体如下: 1. 写一个类继承TextView package com.example.tt; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.TextView; public class ScrollingText

  • Android自定义View实现竖直跑马灯效果案例解析

    首先给出跑马灯效果图 中间的色块是因为视频转成GIF造成的失真,自动忽略哈. 大家知道,横向的跑马灯android自带的TextView就可以实现,详情请百度[Android跑马灯效果].但是竖直的跑马灯效果原生Android是不支持的.网上也有很多网友实现了自定义的效果,但是我一贯是不喜欢看别人的代码,所以这篇博客的思路完全是我自己的想法哈. 首先,我们需要给自定义的控件梳理一下格局,如下图所示: 1.首先我们将控件分为三个区块,上面绿色部分为消失不可见的块,中间黑色部分为可见区域,下面红色部

  • Android 中TextView中跑马灯效果的实现方法

     条件: 1.android:ellipsize="marquee" 2.TextView必须单行显示,即内容必须超出TextView大小 3.TextView要获得焦点才能滚动 mTVText.setText("超过文本长度的数据"); mTVText.setSingleLine(true);设置单行显示 mTVText.setEllipsize(TruncateAt.MARQUEE);设置跑马灯显示效果 TextView.setHorizontallyScrol

  • Android TextView实现跑马灯效果的方法

    本文为大家分享一个非常简单但又很常用的控件,跑马灯状态的TextView.当要显示的文本长度太长,又不想换行时用它来显示文本,一来可以完全的显示出文本,二来效果也挺酷,实现起来超级简单,所以,何乐不为.先看下效果图: 代码实现 TextView自带了跑马灯功能,只要把它的ellipsize属性设置为marquee就可以了.但有个前提,就是TextView要处于被选中状态才能有效果,看到这,我们就很自然的自定义一个控件,写出以下代码: public class MarqueeTextView ex

  • Android实现跑马灯效果的方法

    本文实例讲述了Android实现跑马灯效果的方法.分享给大家供大家参考.具体如下: 运行效果截图如下: 直接在布局里写代码就好了: <TextView android:id="@+id/menu_desc" android:layout_width="300dip" android:layout_height="wrap_content" android:text="温馨提示:左右滑动更改菜单,点击进入" android

  • Android中使用TextView实现文字跑马灯效果

    通常情况下我们想实现文字的走马灯效果需要在xml文件中这样设置 <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" android:ellipsize="marquee" android:focusable="true" android:

  • Android跑马灯MarqueeView源码解析

    跑马灯效果,大家可以去原作者浏览https://github.com/sfsheng0322/MarqueeView 下面看自定义控件的代码 public class MarqueeView extends ViewFlipper { private Context mContext; private List<String> notices; private boolean isSetAnimDuration = false; private OnItemClickListener onIt

  • Android实现垂直跑马灯效果

    在我们开发过程中,跑马灯这个功能非常实用的,在实现这个功能的时候,这个时候我们通常需要找demo来实现这个方法,我从github上面找到这个demo感觉很好用,所以就要实现了这个功能喽MarqueeView,看这个工具类,因为我找这个类的时候是没有点击事件的,所以我给它加了一个点击事件,看这个工具类 public class MarqueeView extends ViewFlipper { private Context mContext; private List<String> noti

  • Android实现图文垂直跑马灯效果

    最近在维护老项目,老项目有一个地方需要修改,就是垂直跑马灯的问题,之前的垂直跑马灯是只有文字跑马灯,新版需要加上. 之前是用的MarqueeView,看了下源代码是只支持文字的,于是我就改了下原作者的源代码. MarqueeView类之前作者的 // 创建ViewFlipper下的TextView private TextView createTextView(CharSequence text, int position) { TextView tv = new TextView(mConte

随机推荐