Android自定义控件(十)——SurfaceView实战实现天气APP背景移动效果

简介: Android自定义控件(十)——SurfaceView实战实现天气APP背景移动效果

SurfaceView与View区别


前面我们所有的讲解基本都是自定义View来实现各种Android的自定义控件,但编写过相机的Android程序员,肯定对SurfaceView不陌生,那什么时候该用SurfaceView呢?


我们先来看一个概念,在Android中屏幕的刷新时间为16ms,如果View能够在16ms内完成所有的执行的绘图操作,那么在视觉上,界面是流畅的;否则APP就会卡顿,我们经常会看到如果View的逻辑非常复杂,Android Studio都会提示以下日志:

Skipped 60 frames! The application maybe doing too much work on its main thread

之所以会提示这个警告,是因为我们在自定义View的绘图操作中,执行了非常复杂的逻辑运算,导致16s内并没有完成绘制,所以当出现在自定义View中非常复杂的耗时的逻辑运算时,就需要使用SurfaceView。


SurfaceView在两个方面改进了View的绘图操作:


1.使用了双缓冲技术


2.自带画布,支持在子线程中更新画布内容


这里说的双缓冲技术,就是多加了一块缓冲画布,当需要执行绘图操作的时候,先在缓冲画布上绘制,绘制好后直接将缓冲画布的内部更新到主画布之中。这样,在屏幕更新的时候,只需要把缓冲画布上的内容照搬过来就可以了,就不会存在耗时的逻辑问题,也解决了超时绘制。


使用缓冲的Canvas绘图


前面我们已经介绍了,SurfaceView时自带画布的,具有双缓冲技术,那么问题来了,我们怎么才能拿到这块画布呢?直接先上代码:

SurfaceHolder surfaceHolder=getHolder();
Canvas canvas=surfaceHodler.lockCanvas();
//中间执行绘图操作
surfaceHolder.unlockCanvasAndPost(canvas);


我们这里直接通过surfaceHolder.lockCanvas()获取到了缓冲画布,并且将画布上锁,防止被其他线程篡改,当绘图完成之后释放锁,通过surfaceHolder.unlockCanvasAndPost(canvas)进行释放,这段代码不仅释放锁,还将缓冲画布的内容更新到主线程的画布上,从而显示到屏幕中。


这里上锁是防止其他线程同时更新缓冲画布,造成缓冲画布乱七八糟,所以我们需要加锁,至于什么是线程锁,死锁,释放锁等知识,这是Java多线程的知识,详情参考Java多线程书籍或者操作系统,这属于基础,篇幅有限,这里就不赘述了。


SurfaceView生命周期


在讲解SurfaceView生命周期之前,我们先要理解三个概念:Surface,SurfaceView,SurfaceHolder。有过MVC开发经验的小伙伴应该会非常熟悉,SurfaceView就是视图V,Surface中保存了缓冲画布和绘制内容相关的各种数据,也就是模型M,SurfaceHolder很明显就是MVC中的C控制器。


所以,当我们需要操作SurfaceView的时候,必然需要Surface存在,所以Android专门提供了监听Surface生命周期的函数:

public class DemoSurfaceView extends SurfaceView {
    private SurfaceHolder surfaceHolder;
    public DemoSurfaceView(Context context) {
        super(context);
    }
    public DemoSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.surfaceHolder=getHolder();
        this.surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
            }
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
        });
    }
    public DemoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}


上面也是每个自定义SurfaceView的基本使用方式,下面小编解释以下Surface的生命周期。


1.surfaceCreated:当Surface对象被创建后,该函数就会调用。


2.surfaceChanged:当surface发生任何结构性变化时,可以时格式,或者大小变化,该函数就会被立即调用。


3.surfaceDestroyed:当surface将要被销毁时调用。


一般来说,我们需要在类初始化时就立即绘图,那么一般放在surfaceCreated中来开启子线程的绘图操作,以防止没被创建时,缓冲画布时空的,在surfaceDestroyed中观察线程是否执行完成,如果没有执行完成,但surface将要被销毁,必须强制取消线程执行。


实现天气APP背景自动左右循环移动效果


为了实现常用的天气APP自动移动背景效果,我们来看看我们首先需要定义哪些成员变量,根据刚才讲的我们需要观察线程在销毁时,线程是否在执行,所以必须定义个线程是否执行的布尔变量,surfaceHolder控制器当然也需要,左右移动只需要X坐标变化,所以也需要定义变化的X坐标值,代码如下:

private SurfaceHolder surfaceHolder;//控制器
private boolean flag=false;//线程是否能执行
private Bitmap bgBitmap;//背景图片
private float screenWidht,screenHeight;//屏幕宽高
private int mBgX;//绘制的X坐标
private Canvas canvas;//画布
private Thread thread;//线程
//定义一个枚举类型,判断移动的方向
private enum State{
  LEFT,RIGHT
}
private State state=State.LEFT;//开始向左运动
private final int MOVE_SIZE=1;//每次移动的距离


因为时左右循环啊移动,送所以我们还定义了枚举类型判断现在时向左还是向右,同时定义画布,屏幕宽高,以及当前运动方向,线程。


其次,我们需要监控Surface的生民周期,所以在其构造函数中调用如下方法进行监控:

public BgAnimSurfaceView(Context context, @Nullable AttributeSet attrs) {
  super(context, attrs);
  this.surfaceHolder=getHolder();
    this.surfaceHolder.addCallback(new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            flag=true;//设置线程可以执行绘图操作
            startAnim();//执行动画
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            flag=false;//设置线程不可以执行绘图操作
        }
    });
}


接着,我们需要将背景图片宽度放大到屏幕的3/2,高度为屏幕高度,所以,我们首先必须将图片定义到指定的大小,用到前面的Bitmap知识,代码如下:

/***
* 执行动画
*/
private void startAnim(){
    this.screenWidht=getWidth();//获取屏幕宽度
    this.screenHeight=getHeight();//获取屏幕高度
    int enlargeWidht=(int) getWidth()*3/2;//放大的倍数
    Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.background);//获取源图片
    this.bgBitmap=Bitmap.createScaledBitmap(bitmap,enlargeWidht,(int)this.screenHeight,true);//将源图片宽度放大3/2倍,生成新的图片
    this.thread=new Thread(new Runnable() {
        @Override
        public void run() {
            while (flag){//如果线程可以执行
                canvas=surfaceHolder.lockCanvas();
                drawView();//绘制
                surfaceHolder.unlockCanvasAndPost(canvas);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    this.thread.start();
}


这段代码就是放大图片,然后执行左右移动,这里使用到了本文第二个知识点,如何使用缓冲画布,而我们将绘制的操作放在了drawView()函数中,这里我们50ms执行一次绘图操作,不设置间隔时间,移动可能很快,达不到慢慢移动的效果,接着我们看看drawView()代码实现:

/***
* 开始绘制
*/
private void drawView(){
    this.canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//先清空屏幕
    this.canvas.drawBitmap(this.bgBitmap,this.mBgX,0,null);//绘制图片
    switch (this.state){//判断现在是向左还是向右移动
        case LEFT:
            this.mBgX-=this.MOVE_SIZE;//向左移动
            break;
        case RIGHT:
            this.mBgX+=this.MOVE_SIZE;//向右移动
            break;
        default:
            break;
    }
    //如果向左移动了1/2,那么更改为向右移动,本身图片宽度只有3/2都移动了1/2显然已经移动完了
    if(this.mBgX<=-this.screenWidht/2){
        this.state=State.RIGHT;
    }
    //如果X坐标大于0,向左移动
    if(this.mBgX>=0){
        this.state=State.LEFT;
    }
}


这样我们就实现了天气APP背景自动移动的效果,代码中的注释已经够详细了,这里就不再赘述了,本文Github下载地址:点击下载

相关文章
|
3月前
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
118 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
21天前
|
数据采集 JSON 网络安全
移动端数据抓取:Android App的TLS流量解密方案
本文介绍了一种通过TLS流量解密技术抓取知乎App热榜数据的方法。利用Charles Proxy解密HTTPS流量,分析App与服务器通信内容;结合Python Requests库模拟请求,配置特定请求头以绕过反爬机制。同时使用代理IP隐藏真实IP地址,确保抓取稳定。最终成功提取热榜标题、内容简介、链接等信息,为分析热点话题和用户趋势提供数据支持。此方法也可应用于其他Android App的数据采集,但需注意选择可靠的代理服务。
75 11
移动端数据抓取:Android App的TLS流量解密方案
|
3月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
97 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
3月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
241 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
3月前
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
104 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
7月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
3月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
82 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
4月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
85 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
5月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
334 11
|
5月前
|
搜索推荐 Android开发 开发者
安卓应用开发中的自定义控件实践
在安卓应用开发的广阔天地中,自定义控件如同璀璨的星辰,点亮了用户界面设计的夜空。它们不仅丰富了交互体验,更赋予了应用独特的个性。本文将带你领略自定义控件的魅力,从基础概念到实际应用,一步步揭示其背后的原理与技术细节。我们将通过一个简单的例子——打造一个具有独特动画效果的按钮,来展现自定义控件的强大功能和灵活性。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往更高阶UI设计的大门。

热门文章

最新文章