Android自定义View实现仿1号店垂直滚动广告条代码

效果图展示,图片有点卡,耐心看会,原程序是很流畅的

实现步骤:

  • 声明变量
  • 初始化画笔、文本大小和坐标
  • onMeasure()适配wrap_content的宽高
  • onDraw()画出根据坐标画出两段Text
  • 监听点击事件
  • 在Activity中实现点击事件

实现原理(坐标变换原理):整个过程都是基于坐标Y的增加和交换进行处理的,Y值都会一直增加到endY,然后进行交换逻辑

步骤一:声明变量

由于1号店是两句话的滚动,所以我们也是使用两句话来实现的

private Paint mPaint;
private float x, startY, endY, firstY, nextStartY, secondY;
//整个View的宽高是以第一个为标准的,所以第二句话长度必须小于第一句话
private String[] text = {"今日特卖:毛衣3.3折>>>", "公告:全场半价>>>"};
private float textWidth, textHeight;
//滚动速度
private float speech = 0;
private static final int CHANGE_SPEECH = 0x01;
//是否已经在滚动
private boolean isScroll = false;

步骤二:初始化画笔、文本大小和坐标

以第一句话为标准来做控件的宽高标准

//初始化画笔
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);
//测量文字的宽高,以第一句话为标准
Rect rect = new Rect();
mPaint.getTextBounds(text[0], 0, text[0].length(), rect);
textWidth = rect.width();
textHeight = rect.height();
//文字开始的x,y坐标
//由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
x = getX() + getPaddingLeft();
startY = getTop() + textHeight + getPaddingTop() - 5;
//文字结束的x,y坐标
endY = startY + textHeight + getPaddingBottom();
//下一个文字滚动开始的y坐标
//由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
nextStartY = getTop() - 5;
//记录开始的坐标
firstY = startY;
secondY = nextStartY;

步骤三:onMeasure()适配wrap_content的宽高

如果学习过自定义View的话,下面的代码应该很熟悉,就是适配warp_content的模板代码:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 int width = measureWidth(widthMeasureSpec);
 int height = measureHeight(heightMeasureSpec);
 setMeasuredDimension(width, height);
}
private int measureHeight(int heightMeasureSpec) {
 int result = 0;
 int size = MeasureSpec.getSize(heightMeasureSpec);
 int mode = MeasureSpec.getMode(heightMeasureSpec);
 if (mode == MeasureSpec.EXACTLY) {
  result = size;
 } else {
  result = (int) (getPaddingTop() + getPaddingBottom() + textHeight);
  if (mode == MeasureSpec.AT_MOST) {
   result = Math.min(result, size);
  }
 }
 return result;
}
private int measureWidth(int widthMeasureSpec) {
 int result = 0;
 int size = MeasureSpec.getSize(widthMeasureSpec);
 int mode = MeasureSpec.getMode(widthMeasureSpec);
 if (mode == MeasureSpec.EXACTLY) {
  result = size;
 } else {
  result = (int) (getPaddingLeft() + getPaddingRight() + textWidth);
  if (mode == MeasureSpec.AT_MOST) {
   result = Math.min(result, size);
  }
 }
 return result;
}

步骤四:onDraw()画出根据坐标画出两段Text(已修复:Text停下来时闪一下的bug)

通过Handler来改变速度

通过isScroll锁,来控制Handler只改变一次

通过invalidate一直重绘两句话的文字

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 //启动滚动
 if (!isScroll) {
  mHandler.sendEmptyMessageDelayed(CHANGE_SPEECH, 2000);
  isScroll = true;
 }
 canvas.drawText(text[0], x, startY, mPaint);
 canvas.drawText(text[1], x, nextStartY, mPaint);
 startY += speech;
 nextStartY += speech;
 //超出View的控件时
 if (startY > endY || nextStartY > endY) {
  if (startY > endY) {
   //第一次滚动过后交换值
   startY = secondY;
   nextStartY = firstY;
  } else if (nextStartY > endY) {
   //第二次滚动过后交换值
   startY = firstY;
   nextStartY = secondY;
  }
  speech = 0;
  isScroll = false;
 }
 invalidate();
}

private Handler mHandler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
  super.handleMessage(msg);
  switch (msg.what) {
   case CHANGE_SPEECH:
    speech = 1f;
    break;
  }
 }
};

步骤五:监听点击事件(已修复:点击事件错乱的问题)

在自定义View重写dispatchTouchEvent处理点击事件,这个也是模板代码:

public onTouchListener listener;
public interface onTouchListener {
 void touchListener(String s);
}
public void setListener(onTouchListener listener) {
 this.listener = listener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
 switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
  case MotionEvent.ACTION_MOVE:
   //点击事件
   if (listener != null) {
    if (startY >= firstY && nextStartY < firstY) {
     listener.touchListener(text[0]);
    } else if (nextStartY >= firstY && startY < firstY) {
     listener.touchListener(text[1]);
    }
   }
   break;
 }
 return true;
}

步骤六:在Activity中实现点击事件

public class VerTextViewActivity extends AppCompatActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_ver_text_view);
  VerTextView tv_ver = (VerTextView) findViewById(R.id.tv_ver);
  tv_ver.setListener(new VerTextView.onTouchListener() {
   @Override
   public void touchListener(String s) {
    Toast.makeText(VerTextViewActivity.this, s, Toast.LENGTH_LONG).show();
   }
  });
 }
}

布局文件

<?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="match_parent"
 android:orientation="horizontal">
 <ImageView
  android:layout_width="120dp"
  android:layout_height="30dp"
  android:background="@drawable/vertextview" />
 <com.handsome.app3.Custom.VerTextView.VerTextView
  android:id="@+id/tv_ver"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="#ffffff"
  android:padding="8dp" />
</LinearLayout>

整个类的源码:

/**
 * =====作者=====
 * 许英俊
 * =====时间=====
 * 2016/10/11.
 */
public class VerTextView extends View {
 private Paint mPaint;
 private float x, startY, endY, firstY, nextStartY, secondY;
 //整个View的宽高是以第一个为标准的,所以第二句话长度必须小于第一句话
 private String[] text = {"今日特卖:毛衣3.3折>>>", "公告:全场半价>>>"};
 private float textWidth, textHeight;
 //滚动速度
 private float speech = 0;
 private static final int CHANGE_SPEECH = 0x01;
 //是否已经在滚动
 private boolean isScroll = false;
 private Handler mHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   switch (msg.what) {
    case CHANGE_SPEECH:
     speech = 1f;
     break;
   }
  }
 };
 public VerTextView(Context context, AttributeSet attrs) {
  super(context, attrs);
  //初始化画笔
  mPaint = new Paint();
  mPaint.setColor(Color.RED);
  mPaint.setTextSize(30);
  //测量文字的宽高,以第一句话为标准
  Rect rect = new Rect();
  mPaint.getTextBounds(text[0], 0, text[0].length(), rect);
  textWidth = rect.width();
  textHeight = rect.height();
  //文字开始的x,y坐标
  //由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
  x = getX() + getPaddingLeft();
  startY = getTop() + textHeight + getPaddingTop() - 5;
  //文字结束的x,y坐标
  endY = startY + textHeight + getPaddingBottom();
  //下一个文字滚动开始的y坐标
  //由于文字是以基准线为基线的,文字底部会突出一点,所以向上收5px
  nextStartY = getTop() - 5;
  //记录开始的坐标
  firstY = startY;
  secondY = nextStartY;
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int width = measureWidth(widthMeasureSpec);
  int height = measureHeight(heightMeasureSpec);
  setMeasuredDimension(width, height);
 }
 private int measureHeight(int heightMeasureSpec) {
  int result = 0;
  int size = MeasureSpec.getSize(heightMeasureSpec);
  int mode = MeasureSpec.getMode(heightMeasureSpec);
  if (mode == MeasureSpec.EXACTLY) {
   result = size;
  } else {
   result = (int) (getPaddingTop() + getPaddingBottom() + textHeight);
   if (mode == MeasureSpec.AT_MOST) {
    result = Math.min(result, size);
   }
  }
  return result;
 }
 private int measureWidth(int widthMeasureSpec) {
  int result = 0;
  int size = MeasureSpec.getSize(widthMeasureSpec);
  int mode = MeasureSpec.getMode(widthMeasureSpec);
  if (mode == MeasureSpec.EXACTLY) {
   result = size;
  } else {
   result = (int) (getPaddingLeft() + getPaddingRight() + textWidth);
   if (mode == MeasureSpec.AT_MOST) {
    result = Math.min(result, size);
   }
  }
  return result;
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  //启动滚动
  if (!isScroll) {
   mHandler.sendEmptyMessageDelayed(CHANGE_SPEECH, 2000);
   isScroll = true;
  }
  canvas.drawText(text[0], x, startY, mPaint);
  canvas.drawText(text[1], x, nextStartY, mPaint);
  startY += speech;
  nextStartY += speech;
  //超出View的控件时
  if (startY > endY || nextStartY > endY) {
   if (startY > endY) {
    //第一次滚动过后交换值
    startY = secondY;
    nextStartY = firstY;
   } else if (nextStartY > endY) {
    //第二次滚动过后交换值
    startY = firstY;
    nextStartY = secondY;
   }
   speech = 0;
   isScroll = false;
  }
  invalidate();
 }
 public onTouchListener listener;
 public interface onTouchListener {
  void touchListener(String s);
 }
 public void setListener(onTouchListener listener) {
  this.listener = listener;
 }
 @Override
 public boolean dispatchTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
   case MotionEvent.ACTION_MOVE:
    //点击事件
    if (listener != null) {
     if (startY >= firstY && nextStartY < firstY) {
      listener.touchListener(text[0]);
     } else if (nextStartY >= firstY && startY < firstY) {
      listener.touchListener(text[1]);
     }
    }
    break;
  }
  return true;
 }
}

以上所述是小编给大家介绍的Android自定义View实现仿1号店垂直滚动广告条代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android仿淘宝首页头条View垂直滚动效果

    之前本来是打算做TextView垂直向上滚动的,后来发现一位大神做得很好,https://github.com/sfsheng0322/MarqueeView 孙福生大神,然后自己要用到多个View向上滚动,也就是类似淘宝首页头条的那种滚动,所以就按照那个思路想了系啊,可以把View拿来滚动,这样可以自己随意的修改View里面的内容,还比较简单一些.所以这个整个思路就是把View就行循环滚动. 看一下循环滚动View的内容咋写的吧,非常简单. package com.dreamlive.upma

  • Android控件ViewFlipper仿淘宝头条垂直滚动广告条

    ViewFlipper的使用,仿淘宝头条垂直滚动广告条,供大家参考,具体内容如下 学习,学习,学以致用 ViewFlipper是安卓自带的控件,很多人可能很少知道这个控件,这个控件很简单,也很好理解,能不能用上实战就看你们的本事了.下面是淘宝头条广告的原效果 下面是我们今天要实现的效果,图片是Gif,运行效果是很流畅的,由于这个图片反应有点慢,会浪费大家点时间,所以我把它调快了,大家可以掏出手机打开淘宝看,一模一样的 从源码可以看出,其实ViewFlipper间接的继承了FrameLayout,

  • android实现上下滚动的TextView

    一 说明    这里重要应用类 AutoTextView,这是一个自定义的类,继承至TextSwitcher,下面临 AutoTextView类做简要说明: 1. 该类应用的重点,在于设置两个动画, setInAnimation(...)  和 setOutAnimation(...),分离是文字进入的动画和文字退出的动画: 2. 类中定义了一个外部类-Rotate3dAnimation,重要靠该类实现文字进出动画,该外部类继承至Animation.说来偶合,这个恰好是在apiDemo中看到了,

  • Android TextView实现垂直滚动效果的方法

    本文实例讲述了Android TextView实现垂直滚动效果的方法.分享给大家供大家参考,具体如下: 在TextView中,如果文本很长,可能需要实现垂直滚动显示文本的效果.这里需要在XML布局文件中为TextView设置如下几个属性. Android:scrollbars="vertical" android:scrollbarStyle="X" 其中X为outsideOverlay或insideOverlay. android:scrollbarFadeDur

  • Android仿即刻首页垂直滚动图,炫酷到底!

    项目地址:https://github.com/JeasonWong/JikeGallery 话不多说,先上效果. 这个效果是在即刻app上看到,觉得很不错,遂仿之. 先说下我的实现思路(以上方的图片滚动为例,下方的文字实现效果类似): 自定义ViewGroup 装载两个ImageView和一个阴影View 通过一定规律交替控制两个ImageView和它们的marginTop,在onLayout()中实现 marginTop的具体值由属性动画控制,不断调用requestLayout() 接下来依

  • android开发之横向滚动/竖向滚动的ListView(固定列头)

    由于项目需要,我们需要一个可以横向滚动的,又可以竖向滚动的 表格.而且又要考虑大数据量(行)的展示视图.经过几天的研究终于搞定,做了一个演示.贴图如下:      好吧.让我们看思路是什么样的: 1. 上下滚动直接使用 listView来实现. 2. 左右滚动使用HorizontalScrollView,来处理滚动.我写一个类MyHScrollView继承 自它. 2.1 . ListView里的每行(row)分为 两部分,不滚动的和可滚动的区域.比如本demo的第一列,就是静态的.而后面的所有

  • Android实现Activity水平和垂直滚动条的方法

    本文实例讲述了Android实现Activity水平和垂直滚动条的方法.分享给大家供大家参考,具体如下: <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="

  • Android中TextView实现垂直滚动和上下滚动效果

    布局里面就是两个自定义的TextView,上面的左右滑动的是AutoHorizontalScrollTextView; 下面上下滚动的是AutoVerticalScrollTextView; 上面左右滑动的非常好实现,直接把AutoHorizontalScrollTextView复制到项目中,复制全类名到布局文件中,和系统TextView一样,只需设置文本其他什么都不用设置: 下面垂直滚动的AutoVerticalScrollTextView相比AutoHorizontalScrollTextV

  • android listview 水平滚动和垂直滚动的小例子

    网上有很多解决 android listview 水平和垂直滚动的代码,我没有按照他们说的做(以前没搜到 O(∩_∩)O~) 我采用的是添加HorizontalScrollViewJava代码 复制代码 代码如下: < ScrollView android:id="@+id/ScrollView01" android:layout_height="300px" android:layout_x="16px" android:layout_y

  • android TextView不用ScrollViewe也可以滚动的方法

    代码 复制代码 代码如下: TextView textview = (TextView) findViewById(R.id.text);            /**             *              * 只有调用了该方法,TextView才能不依赖于ScrollView而实现滚动的效果.             * 要在XML中设置TextView的textcolor,否则,当TextView被触摸时,会灰掉.             */ textview.setMov

随机推荐