Android仿京东首页轮播文字效果

京东客户端的轮播文字效果:

本次要实现的只是后面滚动的文字(前面的用ImageView或者TextView实现即可),看一下实现的效果

实现思路

上图只是一个大概的思路,要实现还需要完善更多的细节,下面会一步步的来实现这个效果:

1.封装数据源:从图上可以看到,轮播的文字是分为两个部分的,暂且把它们分别叫做前缀和内容,而且实际的使用过程中点击轮播图肯定是需要跳转页面的,而且大部分应该是WebView,不妨我们就设置点击时候需要获取的内容就是一个链接,那么数据源的结构就很明了了

创建ADEnity类并完善一些基本的方法,代码如下

public class ADEnity { private String mFront ; //前面的文字 private String mBack ; //后面的文字 private String mUrl ;//包含的链接 public ADEnity(String mFront, String mBack,String mUrl) { this.mFront = mFront; this.mBack = mBack; this.mUrl = mUrl; } public String getmUrl() { return mUrl; } public void setmUrl(String mUrl) { this.mUrl = mUrl; } public String getmFront() { return mFront; } public void setmFront(String mFront) { this.mFront = mFront; } public String getmBack() { return mBack; } public void setmBack(String mBack) { this.mBack = mBack; } }

2.接下来应该是定制这个自定义View了,首先理一下思路,看一个构造图

实现这个自定义View的所有参数都在上表列出了,大部分参数很容易理解,个别参数加进去是很有必要的,比如说是否初始化进入文字的纵坐标,文字是否在移动中这两个参数,之后的内容会详细的叙述一下. 在动手绘制之前还得需要知道一点基础的知识,就是关于绘制文字的方法,里面有很多细节需要处理

方法都比较好理解,绘制指定字符串(可以指定范围)在坐标( x , y )处,但是其中的x,y并不是我们所理解的应该是文字左上角的坐标点.其中的x坐标是根据Paint的属性可变换的,默认的x是文字的左边坐标,如果Paint设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心位置.Y坐标是文字的baseline的y坐标. 关于绘制文字的baseline:

用图来说话吧

图中蓝色的线即为baseline,可以看出他既不是顶部坐标也不是底部坐标,那么当我们绘制文字的时候肯定是希望能把文字绘制在正中间.这时候就要引入paint.getTextBound()方法了 getTextBounds(String text, int start, int end, Rect bounds),传入一个Rect对象,调用此方法之后则会填充这个rect对象,而填充的内容就是所绘制的文字相对于baseline的偏移坐标,将这个Rect加上baseline的坐标,绘制后是这样的:

但其实他的值只是(2,-25,76,3),是相对于baseline的位置,画个图会比较好理解

那么要将文字绘制在中间那么实际绘制baseline的坐标应该是组件的中心加上文字中心即图中框的中间坐标相对于baseline的偏移值">那么要将文字绘制在中间,那么实际绘制baseline的坐标应该是组件的中心,加上文字中心(即图中框的中间坐标)相对于baseline的偏移值

这张图中应该会好理解实际绘制文字的坐标与组件中心坐标的关系.关于偏移值的计算,按常规的几何计算方法,应该是组件的中心坐标+偏移值的绝对值==baseline坐标(即实际绘制的坐标),但是由于框的坐标值都是相对于baseline来计算的,top为负值,botton为正值,那么这个偏移值就可以直接用(top+bottom)/2来表示,没看懂的同学可以画个草图,用top=-25,bottom=3来算一下,看是否结果是一致的.
经过上面的理解,那我们来绘制正确绘制文字的方法也就确定了
已获得组件的高度int mHeight , 文字外框Rect bound的情况下

绘制文字在正中间

mHeight / 2 - (bound.top + bound.bottom) / 2 //在纵坐标为mY的地方绘制文字 //计算方式 //mheight /2 = mY + (bound.top + bound.bottom) / 2 ;

文字滚动到最高点

mY == 0 - bound.bottom //在纵坐标为mY的地方绘制,此时文字刚好移动到最高点 //计算方式 //mY + bound.bottom = 0 ;

文字滚动到最低点,刚好滚出组件

mY = mHeight - indexBound.top; //在纵坐标为mY的地方绘制,此时文字刚好移动到最高点 //计算方式 //mY + bound.top = mHeight ;

知道了如何正确的绘制文字和边界情况的坐标判断,下面就到了绘制文字的步骤了
首先初始化数据,设置默认值

//初始化默认值 private void init() { mDuration = 500; mInterval = 1000; mIndex = 0; mPaintFront = new Paint(); mPaintFront.setAntiAlias(true); mPaintFront.setDither(true); mPaintFront.setTextSize(30); mPaintBack = new Paint(); mPaintBack.setAntiAlias(true); mPaintBack.setDither(true); mPaintBack.setTextSize(30); }

前面的叙述中我们知道,刚开始进入的时候文字应该是位于组件的底部的,但是这个值是需要获取组件的高度和当前显示文字的情况下来判断的,所以应该放在onDraw内来初始化这个值,所以需要前面的是否初始化的属性,判断当mY==0并且未初始化的时候给mY赋值.
接下来就是onDraw内的处理

获取当前的数据

//获取当前的数据 ADEnity model = mTexts.get(mIndex); String font = model.getmFront(); String back = model.getmBack(); // 绘制前缀的外框 Rect indexBound = new Rect(); mPaintFront.getTextBounds(font, 0, font.length(), indexBound); //绘制内容的外框 Rect contentBound = new Rect(); mPaintBack.getTextBounds(back, 0, back.length(), contentBound);

对mY进行初始化

if (mY == 0 && hasInit == false) { mY = getMeasuredHeight() - indexBound.top; hasInit = true; }

对边界情况的处理
/

/移动到最上面 if (mY == 0 - indexBound.bottom) { Log.i(TAG, "onDraw: " + getMeasuredHeight()); mY = getMeasuredHeight() - indexBound.top;//返回底部 mIndex++;//换下一组数据 } //移动到中间 if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) { isMove = false;//停止移动 Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { postInvalidate();//通知重绘 isMove = true;//设置移动为true } }, mInterval);//停顿多少毫秒之后再次移动 } 移动的处理与数据源的处理 mY -= 1;//每次只移动一个像素,尽量保证平滑显示 //循环使用数据 if (mIndex == mTexts.size()) { mIndex = 0; } //如果是处于移动状态时的,则延迟绘制 //计算公式为一个比例,一个时间间隔移动组件高度,则多少毫秒来移动1像素 if (isMove) { postInvalidateDelayed(mDuration / getMeasuredHeight()); } 至此对逻辑的处理就完成了,接下来要设置点击事件 public interface onClickLitener { public void onClick(String mUrl); } private onClickLitener onClickLitener; public void setOnClickLitener(TextViewAd.onClickLitener onClickLitener) { this.onClickLitener = onClickLitener; } //重写onTouchEvent事件,并且要返回true,表明当前的点击事件由这个组件自身来处理 @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //调用回调,将当前数据源的链接传出去 if (onClickLitener != null) { onClickLitener.onClick(mTexts.get(mIndex).getmUrl()); } break; } return true; } 暴露一些其他属性的设置方式 //设置数据源 public void setmTexts(List mTexts) { this.mTexts = mTexts; } //设置广告文字的停顿时间 public void setmInterval(int mInterval) { this.mInterval = mInterval; } //设置文字从出现到消失的时长 public void setmDuration(int mDuration) { this.mDuration = mDuration; } //设置前缀的文字颜色 public void setFrontColor(int mFrontColor) { mPaintFront.setColor(mFrontColor); } //设置正文内容的颜色 public void setBackColor(int mBackColor) { mPaintBack.setColor(mBackColor); } 有兴趣的同学可以将这些属性设置到attrs.xml文件中然后就可以在布局文件中设置属性了,这里就不演示了,因为觉得每次copy这个View还得把xml文件也copy比较麻烦,毕竟as有自动补全,可以很方便的看到暴露在外面的方法.(个人感受而已). 贴一下完整的ADTextView的代码,方便查看 package com.qiyuan.jindongshangcheng.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.TextView; import com.qiyuan.jindongshangcheng.enity.ADEnity; import java.util.List; import java.util.Timer; import java.util.TimerTask; /** * Created by huanghaojie on 2016/9/30. */ public class TextViewAd extends TextView { private int mDuration; //文字从出现到显示消失的时间 private int mInterval; //文字停留在中间的时长切换的间隔 private List<ADEnity> mTexts; //显示文字的数据源 private int mY = 0; //文字的Y坐标 private int mIndex = 0; //当前的数据下标 private Paint mPaintBack; //绘制内容的画笔 private Paint mPaintFront; //绘制前缀的画笔 private boolean isMove = true; //文字是否移动 private String TAG = "ADTextView"; private boolean hasInit = false;//是否初始化刚进入时候文字的纵坐标 public interface onClickLitener { public void onClick(String mUrl); } private onClickLitener onClickLitener; public void setOnClickLitener(TextViewAd.onClickLitener onClickLitener) { this.onClickLitener = onClickLitener; } public TextViewAd(Context context) { this(context, null); } public TextViewAd(Context context, AttributeSet attrs) { super(context, attrs); init(); } //重写onTouchEvent事件,并且要返回true,表明当前的点击事件由这个组件自身来处理 @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //调用回调,将当前数据源的链接传出去 if (onClickLitener != null) { onClickLitener.onClick(mTexts.get(mIndex).getmUrl()); } break; } return true; } //设置数据源 public void setmTexts(List mTexts) { this.mTexts = mTexts; } //设置广告文字的停顿时间 public void setmInterval(int mInterval) { this.mInterval = mInterval; } //设置文字从出现到消失的时长 public void setmDuration(int mDuration) { this.mDuration = mDuration; } //设置前缀的文字颜色 public void setFrontColor(int mFrontColor) { mPaintFront.setColor(mFrontColor); } //设置正文内容的颜色 public void setBackColor(int mBackColor) { mPaintBack.setColor(mBackColor); } //初始化默认值 private void init() { mDuration = 500; mInterval = 1000; mIndex = 0; mPaintFront = new Paint(); mPaintFront.setAntiAlias(true); mPaintFront.setDither(true); mPaintFront.setTextSize(30); mPaintBack = new Paint(); mPaintBack.setAntiAlias(true); mPaintBack.setDither(true); mPaintBack.setTextSize(30); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.i(TAG, "onSizeChanged: " + h); } @Override protected void onDraw(Canvas canvas) { if (mTexts != null) { Log.i(TAG, "onDraw: " + mY); //获取当前的数据 ADEnity model = mTexts.get(mIndex); String font = model.getmFront(); String back = model.getmBack(); // 绘制前缀的外框 Rect indexBound = new Rect(); mPaintFront.getTextBounds(font, 0, font.length(), indexBound); //绘制内容的外框 Rect contentBound = new Rect(); mPaintBack.getTextBounds(back, 0, back.length(), contentBound); //刚开始进入的时候文字应该是位于组件的底部的 ,但是这个值是需要获取组件的高度和当前显示文字的情况下来判断的, // 所以应该放在onDraw内来初始化这个值,所以需要前面的是否初始化的属性,判断当mY==0并且未初始化的时候给mY赋值. if (mY == 0 && hasInit == false) { mY = getMeasuredHeight() - indexBound.top; hasInit = true; } //移动到最上面 if (mY == 0 - indexBound.bottom) { Log.i(TAG, "onDraw: " + getMeasuredHeight()); mY = getMeasuredHeight() - indexBound.top;//返回底部 mIndex++;//换下一组数据 } canvas.drawText(back, 0, back.length(), (indexBound.right - indexBound.left) + 20, mY, mPaintBack); canvas.drawText(font, 0, font.length(), 10, mY, mPaintFront); //移动到中间 if (mY == getMeasuredHeight() / 2 - (indexBound.top + indexBound.bottom) / 2) { isMove = false;//停止移动 Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { postInvalidate();//通知重绘 isMove = true;//设置移动为true } }, mInterval);//停顿多少毫秒之后再次移动 } //移动的处理与数据源的处理 mY -= 1;//每次只移动一个像素,尽量保证平滑显示 //循环使用数据 if (mIndex == mTexts.size()) { mIndex = 0; } //如果是处于移动状态时的,则延迟绘制 //计算公式为一个比例,一个时间间隔移动组件高度,则多少毫秒来移动1像素 if (isMove) { postInvalidateDelayed(mDuration / getMeasuredHeight()); } } } } 怎么使用呢? 1,现在xml文件里引入这个自定义控件 <?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="vertical"> <com.qiyuan.jindongshangcheng.view.TextViewAd android:id="@+id/textad" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> 2.在MainActivity中使用 /** * Created by huanghaojie on 2016/9/30. */ public class MainActivity extends Activity { private TextViewAd textViewAd; private List<ADEnity> mList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main4); textViewAd = (TextViewAd) findViewById(R.id.textad); mList = new ArrayList<>(); for (int i = 0; i < 10; i++) { ADEnity adEnity = new ADEnity("前缀" + i, "后缀" + i, "http://www.baidu.com"+i); mList.add(adEnity); } textViewAd.setmTexts(mList); textViewAd.setFrontColor(Color.RED); textViewAd.setBackColor(Color.BLUE); textViewAd.setmDuration(1000); textViewAd.setmInterval(1000); textViewAd.setOnClickLitener(new TextViewAd.onClickLitener() { @Override public void onClick(String mUrl) { Toast.makeText(MainActivity.this,"点击了"+mUrl,Toast.LENGTH_LONG).show(); } }); } }

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

时间: 2024-10-28 02:24:14

Android仿京东首页轮播文字效果的相关文章

Android仿京东首页轮播文字效果_Android

京东客户端的轮播文字效果: 本次要实现的只是后面滚动的文字(前面的用ImageView或者TextView实现即可),看一下实现的效果 实现思路 上图只是一个大概的思路,要实现还需要完善更多的细节,下面会一步步的来实现这个效果: 1.封装数据源:从图上可以看到,轮播的文字是分为两个部分的,暂且把它们分别叫做前缀和内容,而且实际的使用过程中点击轮播图肯定是需要跳转页面的,而且大部分应该是WebView,不妨我们就设置点击时候需要获取的内容就是一个链接,那么数据源的结构就很明了了 创建ADEnity

利用AngularJs实现京东首页轮播图效果_AngularJS

先来看看效果图 其实写一个轮播图还是蛮简单的,我想了两种种方法,来实现轮播图(实际上细分是5种,但是其中两种是操作dom原生,三种是利用AngularJs的动画,所有归为两大类),等我写出来,大家好好理解一下就好. 那我先写一种,第一种是不使用angularjs的动画模块,只使用指令来完成动画的切换.在这里面就是在指令里操作dom元素,超级easy. 示例代码 <!DOCTYPE html> <html lang="en" ng-app="lunbo&quo

Android 仿今日头条简单的刷新效果实例代码_Android

点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感觉还不错,整理一下. MainActivity package com.example.fragmentmytest; import android.content.DialogInterface; import android.graphics.Color; import android.os.B

Android 仿今日头条简单的刷新效果实例代码

点击按钮,先自动进行下拉刷新,也可以手动刷新,刷新完后,最后就多一行数据.有四个选项卡. 前两天导师要求做一个给本科学生预定机房座位的app,出发点来自这里.做着做着遇到很多问题,都解决了.这个效果感觉还不错,整理一下. MainActivity package com.example.fragmentmytest; import android.content.DialogInterface; import android.graphics.Color; import android.os.B

Android 使用ViewPager实现轮播图效果

写这篇文章只是对今天所学的知识进行加深印象,对ViewPager的一些处理,比如适配器和各个方法的作用等. 先看效果图 这里我是在xml中写的圆点 Drawable文件夹下的xml代码: Shape_yes.xml: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android">

Android仿今日头条滑动页面导航效果_Android

最近项目中用到了滑动页面,也就是和目前市场上很火的"今日头条"页面滑动类似,在网上找了一下,大部分都是用ViewPager来实现的,刚开始我用的是ViewPager+ViewGroup,上面的标题按钮用的是HorizontalScrollView,写完之后感觉效果比较生硬,果断换掉,发现了一个效果比较好的第三方,也就是今天的主题:PagerSlidingTabStrip.好了,下面来具体介绍一下PagerSlidingTabStrip,进行一下源码解析. 一.看一下demo的样子吧 二

Android仿网易客户端顶部导航栏效果_Android

最近刚写了一个网易客户端首页导航条的动画效果,现在分享出来给大家学习学习.我说一下这个效果的核心原理.下面是效果图:          首先是布局,这个布局是我从网易客户端反编译后弄来的.大家看后应该明白,布局文件如下: <FrameLayout android:id="@id/column_navi" android:layout_width="fill_parent" android:layout_height="wrap_content&quo

Android仿简书动态searchview搜索栏效果

简书的动态搜索栏效果是这样的,挺高大上的感觉. ezgif.com-resize.gif 仔细想一下,其实实现起来非常简单,这是我做的效果,基本完美还原. ezgif.com-resize (2).gif 实现这个效果, 只要关注几个点 1.搜索栏伸展和收缩动画效果实现 2.搜索栏伸展和收缩的时机 3.顶部透明度的渐变 搜索栏伸展和收缩动画效果实现: 我们只要明确,使用系统为我们提供的Transition框架,就可以轻而易举的实现了. 首先要引入依赖compile 'com.android.su

Android仿探探卡片式滑动效果实现

前言 第一次进入探探软件界面,就被这种通过卡片式滑动来选择"喜欢/不喜欢"的设计所吸引了.当时就非常想通过自己来实现这种仿探探式的效果,然而却没什么思路.不过毋庸置疑的是,这种效果的原理肯定和 ListView / RecyclerView 类似,涉及到 Item View 的回收和重用,否则早就因为大量的 Item View 而 OOM 了. 再到后来,看到许多大神也推出了同样仿探探效果的博客,从头到尾阅读下来,写得通俗易懂,基本上没什么问题.于是,实现仿探探效果的想法再次出现在脑海