实例演示自定义View和Scroller的使用

在之前的文章中,已经介绍过View的坐标系,滑动等相关内容。今天结合一个具体的例子来演示一下。
首先介绍一下Scroller。

Scroller

Scroller是一个滑动的辅助类,它主要包含以下内容

  1. startScroll(int startX, int startY, int dx, int dy, int duration)
    startX: 滑动开始的X位置
    dx: 将要滑动的距离
    duration:在多长时间内完成滑动

  2. getScrollX(),getScrollY()
    获取滑动距离

需要注意的是,在Scroll相关的方法中,包括scrollTo,scrollBy,getScrollX(),startScroll,他们的参数是有方向的,与View的坐标系不同,向右,向下为负值,反之为正值。

举个例子:
首先通过scrollTo(100,50)将View的内容滑动,View的位置会向左,上移动 100,50 个像素。此时,通过getScrollX/Y()获取到的值,就是 100 , 50

然后通过scrollBy(-20, -10)再移动一下View,此时View会向右,下移动 20,10 个像素,然后通过getScrollX/Y()获取到的值为100-20=80,50-10=40,View的内容位于初始位置的左上方。

简单的总结一下:

getScrollX/Y()得到的是当前View的内容与View的初始位置之间的距离,右下为负,反之为正。
startScroll(int startX, int startY, int dx, int dy, int duration):
startX/Y表示滑动开始的位置(通常由getScrollX/Y()得到)
dX/Y表示将要滑动的距离,右下为负,反之为正。
稍后会在具体的例子中演示。

  1. invalidate();
    invalidate();是View中的方法,会调用draw,进行View重绘,跟在startScroll方法之后。startScroll实际上只是告诉View怎样滑动,invalidate()之后才会开始滑动。

  2. 重写computeScroll()
    这个方法才是实现弹性滑动的关键。弹性滑动,可以理解为平滑的移动,我们通过scrollTo/By,实现的滑动是一下就完成的,弹性滑动是缓慢平滑的移动。computeScroll()是怎样实现弹性滑动的呢?
    看一下代码就明白了:

    @Override
    public void computeScroll() {
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

computeScroll是在draw方法中调用的,startScroll之后的invalidate会调用draw方法,draw又调用了computeScroll,if里的条件mScroller.computeScrollOffset()是判断滑动是否完成,如果没有完成,就会调用scrollTo,将view滑动到当前位置,然后再postInvalidate继续调用draw方法,最终将View一点一点移动到目标位置。

实例演示

我们要实现的是一个很简单的例子,自定义一个圆形,当手指按下的时候,圆形移动到手指的位置,手指移动的时候,圆形会跟随手指移动,抬起手指的时候,圆形慢慢的回复到初始位置。
代码如下:

public class FollowFingerView extends View{

    //绘制圆形
    private Paint mPaint;

    //绘制背景
    private Paint mBackGroundPaint;
    //View的宽和高
    private int mWidth;
    private int mHeight;
    //定义Scroller
    private Scroller mScroller;
    //存储上次View的位置参数
    private int mLastX;
    private int mLastY;
    
    public FollowFingerView(Context context) {
        this(context,null);
        // TODO Auto-generated constructor stub
    }
    public FollowFingerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        // TODO Auto-generated constructor stub
    }
    private void init(){
        mPaint = new Paint();
//设置圆形颜色
        mPaint.setColor(0x22ff0000);
        mBackGroundPaint=new Paint();
//设置背景颜色
        mBackGroundPaint.setColor(0xfff8efe0);
        mScroller = new Scroller(getContext());
//设置默认宽高,wrap_content时使用
        mWidth = 400;
        mHeight = 400;
    }
@Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
//绘制背景
        canvas.drawPaint(mBackGroundPaint);
    //绘制半径为30像素的圆形
        int radius = 30;
        canvas.drawCircle(30,  30, radius, mPaint);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
//处理wrap_content失效
        setMeasuredDimension(measureWidth(widthMode,width), measureHeight(heightMode,height));
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
  //获取触发事件时,手指的触碰位置
        int x = (int) event.getX();
        int y = (int) event.getY();
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //手指按下时,移动View到触碰位置
            scrollTo(-x, -y);
            break;
        case MotionEvent.ACTION_MOVE:
//事件发生时手指的位置与上一个事件结束时手指的位置,之间的距离
//注意方向,可能这样写更容易理解:
//int dx = -(x - mLastX);
//int dy = -(y - mLastY);
            int dy = mLastY - y;
            int dx = mLastX - x;
            int dy = mLastY - y;
            //跟随手指移动
            scrollBy(dx, dy);
            break;
            
        case MotionEvent.ACTION_UP:
//手指抬起的时候,开始返回初始位置
            mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), 500);
            Log.d("Follow", "getScrollX is " + getScrollX());
            invalidate();
            break;

        default:
            break;
        }
//记录事件结束时手指的位置
        mLastX = x;
        mLastY = y;
    //该View不是clickable的,返回值默认为false,应该手动改为true,否则不能消费事件,只能执行down
    //return super.onTouchEvent(event);
        return true;
    }

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    private int measureWidth(int widthMode,int widthSize){
        switch (widthMode) {
        
        case MeasureSpec.EXACTLY:
            mWidth=widthSize;
            //Log.d("ViewConstructor", "mode is exactly");
            break;
        
        case MeasureSpec.UNSPECIFIED:
        case MeasureSpec.AT_MOST:
            break;
        default:
            break;
        }
        return mWidth;
    }
    
    private int measureHeight(int heightMode,int heightSize){
        
        switch (heightMode) {
        
        case MeasureSpec.EXACTLY:
            mHeight=heightSize;
            break;
            
        case MeasureSpec.UNSPECIFIED:
        case MeasureSpec.AT_MOST:
            break;
        default:
            break;
        }
        return mHeight;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容

  • 内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。 当了解了Android坐标系和触...
    Blankj阅读 6,639评论 3 61
  • 一、Android开发初体验 二、Android与MVC设计模式模型对象存储着应用的数据和业务逻辑。模型类通常用来...
    为梦想战斗阅读 885评论 0 3
  • 背景 这是一个滑动帮助类,并不可以使View真正的滑动,而是根据时间的流逝,获取插值器中的数据,传递给我们,让我们...
    anmi7阅读 776评论 0 1
  • 长歌当哭,为生命中那些终将逝去的人和事化为尘土;长歌当哭,为那些似水流年、如花美眷,终散作云烟。 流光容易把人抛,红了
    忽悠你的歌阅读 248评论 0 1
  • 01 风和,日暖。 苏柒的心情极差。这已经是她相的第三十次亲了。 苏柒看着镜子里的自己,那张脸眉目清秀,个子不高,...
    木陶眠阅读 281评论 3 6