虽然说界面美观一直都不是程序员的菜,但是我们必须承认这是个看脸的世界。
现在的android程序员招聘也基本都会要求要会自定义控件和实现各种酷炫吊炸天的UI效果。
美工呢除了给你几套切图也帮不了我们了,因为很多效果都是与逻辑相关的,都得靠代码来实现。
所以呢我准备写番外篇来巩固自己的知识和记录一些自己实现的或别人实现的酷炫效果。
废话少说,第一篇先来个开胃菜,来个我自己写的(炒鸡简单)的效果:
不一样的走马灯(ForeverTextView)
走马灯是当文字内容过长,而一行显示不完时,用滚动文字来达到显示全部内容的效果。android原生的TextView已经支持这种效果了,不过跟本猿实现的效果不同。简单的说就是,原生的只支持循环滚动,而今天我们要实现的是来回的滚,不停的滚。先上图:
实现效果:
(1)文字来回滚动的走马灯效果
(2)支持padding设置
(3)支持文字颜色大小和滚动速度设置
(4)支持滚动到两端是的停顿时间设置
实现思路:
首先呢,个人理解:自定义控件分为两类。
(1)继承View类的,主要重写onDraw(Canvas canvas)方法,然后在canvas(画布)上可以胡搞瞎搞,胡搞瞎搞。
(2)继承已有控件的,主要是对原生控件的扩展或组合,这一种用的会比较多。
而这一篇呢就是要讲第一种,那么就先来看ForeverTextView的onDraw(Canvas canvas)方法:
protected void onDraw(Canvas canvas) {
// 裁剪出显示文字的区域
canvas.clipRect(getPaddingLeft(), getPaddingTop(), mWidht - getPaddingRight(), mHeight - getPaddingBottom());
// 把文字内容画出来
canvas.drawText(mText, mCurrentOffset + getPaddingLeft(), mHeight - mPaint.descent() - getPaddingBottom(),
mPaint);
}
说了炒鸡简单,一共两句。重写View的自定义控件一般的难点是在计算方面,比如大小,位置。我在onDraw里只是把文字给画出来一次。所以要达到滚动效果其实就是不断的请求重绘,并且每次重绘时计算出新的位置。具体就是不断的计算drawText(String text, float x, float y, Paint paint)的x参数,让这个值从递增到递减,以实现来回滚动的效果。记住当你画的内容超过canvas的范围时,在外围的内容为不可见的,并且这里我们用clipRect将中间区域裁剪出来,让内容在padding的留白区域也不可见。
我们使用Handler来进行不断请求重绘,并且每次重绘前先计算出新的位置。
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO 自动生成的方法存根
switch (msg.what) {
case 101:
// 如果已经滚动到末尾,改动mOffset的正负来改变滚动方向(- 3 * mSpeed是因为有时最后一个文字会显示不全)
if (mContentWidht - mCurrentOffset - 3 * mSpeed > mTextWidht) {
mHandler.sendEmptyMessageDelayed(101, mPauseEnd > 0 ? mPauseEnd : 60);
mOffset = 0 - mOffset;
}
// 如果已经滚动到开始,改动mOffset的正负来改变滚动方向
else if (mCurrentOffset > 0) {
mHandler.sendEmptyMessageDelayed(101, mPauseStart > 0 ? mPauseStart : 60);
mOffset = 0 - mOffset;
}
// 没到两端,继续滚
else {
mHandler.sendEmptyMessageDelayed(101, 60);
}
invalidate();
// 记录已偏移量
mCurrentOffset += mOffset;
break;
}
return false;
}
});
代码很简单,每60毫秒请求一次重绘,主要是滚动到两端了要改变滚动的方向,至于怎么判断的,不是我们讲的重点,有兴趣的可以自己研究下,很简单的,只要知道mTextWidht(文本长度)是整段文本的长度,并不只是可见区域的长度。
mOffset是每次重绘的叠加偏移量,为负时向左滚,反之向右滚,通过mOffset的正负性来控制滚动方向,通过mOffset的绝对值大小来控制滚动速度。
还有一点要注意的就是继承View类的自定义控件一般都需要重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法来确定自己的大小。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 计算出应该占的高度
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) (mPaint.descent() - mPaint.ascent() + getPaddingTop() + getPaddingBottom()), MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidht = getMeasuredWidth();
mHeight = getMeasuredHeight();
mContentWidht = mWidht - getPaddingLeft() - getPaddingRight();
// 若没有处于滚动状态并且文字宽度大于显示区域宽度,开始滚动
if (!isScrolling) {
if (mTextWidht > mContentWidht) {
isScrolling = true;
mHandler.sendEmptyMessage(101);
}
}
}