原生Android 以面向对象的方式操作canvas

news2025/7/19 21:01:57

Android 自定义view 用canvas去画图形, 都是以面向过程的方式去一笔一笔的画, 而且画的图形也不能支持添加事件, 而html, js在这方面有大量的封装好的canvas框架, 很奇怪的是android上我也没有搜到类似的封装框架, 我只是个web前端开发者, 可能是我对android不了解没有搜索到, 我就仿照html,js这一套实现了这个Android上的canvas小框架object-canvas。

框架参照html div标签的一些特性实现的

  1. 有盒子模型,支持border,padding, 可以分别设置上下左右的样式
  2. 可以直接设置文本, 在android里显示个文字还要嵌套上一层textview
  3. 仅支持绝对布局, 这个得需要自己计算元素的位置了, 确定元素的宽,高l,eft和top, 相对布局还没实现
  4. 支持添加事件, 可以捕获和冒泡事件
  5. 直接支持滚动条, 在android里还要嵌套一个ScrollView才出滚动条
  6. 支持transform变换,支持平移, 旋转,缩放等, 支持全局坐标转自身坐标, 自身坐标转全局坐标
  7. 支持缓动动画, 支持属性动画和timeline, 动画参数可以设置往返执行, 执行次数, 缓动函数等

当前这个框架只是抛转引玉, 没啥实用性

开源代码: 

object-canvasicon-default.png?t=M85Bhttps://github.com/chengxg/object-canvas

这个图就是以canvas来画的demo

 CanvasDemoView.java

package com.github.chengxg.object_canvas;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.github.chengxg.object_canvas.Anime;
import com.github.chengxg.object_canvas.Body;
import com.github.chengxg.object_canvas.Element;
import com.github.chengxg.object_canvas.Event;
import com.github.chengxg.object_canvas.shape.CicleShape;
import com.github.chengxg.object_canvas.shape.ImageShape;

public class CanvasDemoView extends View {
    public Body root = null;
    public int screenWidth = 0;
    public int screenHeight = 0;
    public float screenWidthDp = 0;
    public float screenHeightDp = 0;

    public CanvasDemoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics dm = getResources().getDisplayMetrics();
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
        screenWidthDp = screenWidth / dm.density;
        screenHeightDp = screenHeight / dm.density;
        root = new Body(this);
        root.setDensityScale(dm.density);
        root.layout.setContent((float) screenWidthDp, (float) screenHeightDp);
        root.setBox().setBackgroundColor(0xfff2f2f2);
        initPage(root);
    }

    public void initPage(Element parent) {
        //@formatter:off
        Element page = new Element(); parent.addChild(page);
            Element section1 = new Element();page.addChild(section1);
                Element title = new Element();section1.addChild(title);
                Element content = new Element();section1.addChild(content);
                    ImageShape leftCnt = new ImageShape();content.addChild(leftCnt);
                    Element rightCnt = new Element();content.addChild(rightCnt);
                        Element cntTitle = new Element();rightCnt.addChild(cntTitle);
                        Element cntDesc = new Element();rightCnt.addChild(cntDesc);
            Element sectionAnimate = new Element();page.addChild(sectionAnimate);
            Element sectionScroll = new Element();page.addChild(sectionScroll);
        //@formatter:on

        page.getLayout().setPadding(10).setContentMatchParent(0);
        page.setScroll();

        section1.setName("testsection1").getLayout().setBorderWidth(1).setPadding(10).setContentMatchParent(150);
        section1.setBox().setBackgroundColor(Color.WHITE).setBorderColor(0xffcccccc).setBorderRadius(10);

        title.setName("testTitle").getLayout().setPadding(10, 5).setBorderBottom(1).setContentMatchParent(40);
        title.setTextContent("Section1").setColor(0xff333333).setTextSize(16);
        title.setBox().setBorderColor(0xffcccccc);

        content.getLayout().setPadding(5).setContentMatchParent(80).setPosition(0, title.layout.top + title.getLayout().getHeight() + 10);

        Bitmap picIcon = BitmapFactory.decodeResource(this.getResources(), R.drawable.picture_icon);
        leftCnt.getLayout().setContent(48, 48);
        leftCnt.setBitmap(picIcon);

        rightCnt.getLayout().setPosition(60, 0).setContent(content.layout.contentWidth - 60, 60);

        cntTitle.getLayout().setPadding(0, 0).setBorderWidth(1).setContentMatchParent(30);
        cntTitle.setTextContent("标题").setColor(0xff333333).setTextSize(16);
        cntTitle.setBox().setBorderColor(0xffcccccc);

        cntDesc.getLayout().setPadding(0, 0).setBorderWidth(1).setContentMatchParent(24).setPosition(0, 30);
        cntDesc.setTextContent("内容描述").setColor(0xff666666).setTextSize(14);
        cntDesc.setBox().setBorderColor(0xffcccccc);

        // 动画
        sectionAnimate.getLayout().setBorderWidth(1).setPadding(10).setPosition(0, section1.getLayout().getHeight() + section1.getLayout().top + 15).setContentMatchParent(300);
        sectionAnimate.setBox().setBackgroundColor(Color.WHITE).setBorderColor(0xffcccccc).setBorderRadius(10);
        float width = sectionAnimate.getLayout().getWidth();
        float height = sectionAnimate.getLayout().getHeight();
        for (int i = 0; i < 500; i++) {
            CicleShape cicle = new CicleShape();
            sectionAnimate.addChild(cicle);
            int color = ((int) (Math.random() * 0xffffff)) | 0xff000000;
            cicle.setFill(color);
            cicle.setR((float) (30 * Math.random() + 5)).setCenter((float) (width * Math.random()), (float) (height * Math.random()));
            cicle.getParams().put("dir", color % 2 == 0 ? 1 : -1);
        }
        Anime.Instance anime = root.anime.create("{loopCount:0,duration:100,isGoBack:true,easing:'easeInOutQuart',change:null}", null);
        anime.change = (double px, Anime.Instance animate, Anime.PropKeyFrame propKeyFrame) -> {
            if (sectionAnimate.childs != null) {
                for (Element item : sectionAnimate.childs) {
                    CicleShape cicle = (CicleShape) item;
                    int dir = (int) cicle.getParams().get("dir");
                    float cx = cicle.x + dir * (float) (Math.random());
                    float cy = cicle.y + dir * (float) (Math.random());
                    if (cx < 0 || cx > width) {
                        dir = dir * -1;
                    }
                    if (cy < 0 || cy > height) {
                        dir = dir * -1;
                    }
                    cicle.getParams().put("dir", dir);
                    cicle.setCenter(cx, cy);
                }
            }
            root.setUpdateView();
        };
        anime.restart();

        //滚动条
        sectionScroll.getLayout().setBorderWidth(1).setPadding(10).setPosition(0, sectionAnimate.getLayout().getHeight() + sectionAnimate.getLayout().top + 15).setContentMatchParent(300);
        sectionScroll.setBox().setBackgroundColor(Color.WHITE).setBorderColor(0xffcccccc).setBorderRadius(10);
        for (int i = 0; i < 20; i++) {
            //@formatter:off
            Element item = new Element();
            sectionScroll.addChild(item);
            Element text = new Element();
            item.addChild(text);
            //@formatter:on
            float itemHeight = 36;
            item.getLayout().setPadding(5).setBorderWidth(1).setBoxSize(sectionScroll.layout.contentWidth, itemHeight).setPosition(0, (itemHeight + 5) * i + 5);
            item.setBox().setBorderColor(0xffcccccc).setBorderRadius(10);

            text.getLayout().setContentMatchParent(14);
            text.setTextContent("scroll item" + i).setTextSize(14).setColor(Color.GREEN);

            final int idx = i;
            item.setSilent(true).getEvent().on(Event.Touchstart, (Event event) -> {
                event.current.setBox().setBackgroundColor(0xffe0e0e0);
                return false;
            }).on(Event.Touchend, (Event event) -> {
                event.current.setBox().setBackgroundColor(Color.WHITE);
                return false;
            }).on(Event.Click, (Event event) -> {
                Log.d("click", "item" + idx);
                return false;
            });
        }
        sectionScroll.setScroll().updateScrollSize();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        long t = System.currentTimeMillis();
        root.render(canvas);
        long end = System.currentTimeMillis();
        //Log.d("onDraw", (end - t) + "ms");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        root.dispatchEvent(event);
        return true;
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(screenWidth, screenHeight);
    }
}
MainActivity.java
package com.github.chengxg.object_canvas;

import android.app.Activity;
import android.os.Bundle;
import android.widget.FrameLayout;

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);

        FrameLayout layout = new FrameLayout(this);
        CanvasDemoView demoView = new CanvasDemoView(this, null);
        demoView.setLayoutParams(layoutParams);
        layout.addView(demoView);

        this.addContentView(layout, layoutParams);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/35951.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nodejs中包的介绍及npm安装依赖包的多种方法

文章目录1 包的介绍1.1 什么是包1.2 包的来源1.3 为什么需要包1.4 从哪里下载包1.5 如何下载包2 npm2.1 npm安装依赖包2.2 装包后多了哪些文件2.3 安装指定版本的包1 包的介绍 1.1 什么是包 Nodejs中的第三方模块又叫做包 就像电脑和计算机指的是相同的东西&#xff0c;第三…

Wireshark Ethernet and ARP 实验—Wireshark Lab: Ethernet and ARP v7.0

Wireshark Lab: Ethernet and ARP v7.0 1. Capturing and analyzing Ethernet frames 清除浏览器缓存 使用wireshark抓包并请求网页 修改“捕获数据包列表”窗口&#xff0c;仅显示有关 IP 以下协议的信息。 抓包干扰较多&#xff0c;故分析作者的数据包回答下列问题 包含…

Apollo 应用与源码分析:Monitor监控 - 基本概念与入口分析

Monitor 系统监控 目录 基本概念 代码结构分析 整体逻辑分析 基本概念 整体分类 该模块包含用于检查硬件状态和监视系统运行状况的代码等系统级软件。 在Apollo 5.5中&#xff0c;监视模块现在执行以下检查: 运行模块状态监控数据完整性监控数据频率监视系统运行状况(例…

springboot 点滴(3)springboot ThreadLocal实现单机权限认证

Springboot中权限认证的基本方案&#xff1a; 可以对URL进行HandlerInterceptor拦截&#xff0c;然后权限验证。 Client登录成功后&#xff0c;每次发送请求时&#xff0c;会将token等信息存放到header中。 Server收到请求&#xff0c;在HandlerInterceptor中从header获取用户…

3.4、可靠传输

3.4、可靠传输 3.4.1、基本概念 使用差错检测技术\color{red}差错检测技术差错检测技术&#xff08;例如循环冗余校验 CRC )&#xff0c;接收方的数据链路层就可检测出帧在传输过程中是否产生了误码\color{red}误码误码&#xff08;比特错误)。 数据链路层向上层提供的服务类…

hive中连续N天登录问题、topN问题、拉链表实现

一、连续N天登录问题 一般采用开窗函数来实现 首先需要用到窗口函数的向下取值 窗口函数lead 功能&#xff1a;用于从当前数据中基于当前行的数据向后偏移取值 语法&#xff1a;lead(colName&#xff0c;N&#xff0c;defautValue) colName&#xff1a;取哪一列的值 N&#xf…

RemObjects Remoting SDK for Delphi

RemObjects Remoting SDK for Delphi 远程处理SDK是一个框架&#xff0c;允许您创建使用服务公开性能的服务器。然后&#xff0c;可以通过网络远程访问这些解决方案&#xff0c;这些解决方案可以通过运行在大量语言和平台上的客户端软件来实现。 在当今时代&#xff0c;许多程序…

【人才盘点九宫格】你还不来学一学人才九宫格,知道你在领导心目中是属于哪一类人才吗?

文章目录一、九宫格的维度——绩效、能力、潜力1.1 维度一&#xff1a;绩效。1.2 维度二&#xff1a;能力。1.3 维度三&#xff1a;潜力。二、九宫格分类2.1 经典九宫格&#xff1a;绩效-能力九宫格2.2 高潜九宫格&#xff1a;绩效-潜力九宫格2.2.1 高潜九宫格图示2.2.2 高潜九…

SRV1:拥有一个阿里云服务器

1.1 简介 平台&#xff1a; 阿里云   时间&#xff1a; 2022.11.25   类型&#xff1a; 轻量应用服务器2核2G   链接&#xff1a; https://www.aliyun.com/daily-act/ecs/activity_selection?utm_contentse_1013075595 1.2 说明 本文购买的是阿里云的 轻量应用服务器2…

NFT 的洗盘交易,真的赚钱吗?

Nov. 17&#xff0c;Hanson Data Source&#xff1a; Footprint Analytics Wash Trade Analysis 洗盘交易造成的虚假交易是准确评估 NFT 项目、交易所和整个行业的最大障碍之一。 这份报告将会分析X2Y2市场的洗盘交易者如何从洗盘交易和代币质押中获励。同时分析了前10交易量…

3.3、差错检测

3.3、差错检测 3.3.1、比特差错 比特差错\color{red}比特差错比特差错 实际的通信链路都不是理想的&#xff0c;比特在传输过程中可能会产生差错: 111 可能会变成 000 ,而 000 也可能变成 111 。 比特流在传输过程中由于受到各种干扰&#xff0c;就可能会出现比特差错&#…

K8S基础知识学习

目录 一、什么是 Kubernetes &#xff1f; Kubernetes是Google在2014年开源的一个容器集群管理系统&#xff0c;Kubernetes简称K8S。 K8S用于容器化应用程序的部署&#xff0c;扩展和管理。 K8S提供了容器编排&#xff0c;资源调度&#xff0c;弹性伸缩&#xff0c;部署管理&a…

详解设计模式:简单工厂模式

简单工厂模式&#xff08;Smiple Factory Pattern&#xff09;&#xff1a;定义一个工厂类&#xff0c;他可以根据参数的不同返回不同类的实例&#xff0c;被创建的实例通常都具有共同的父类&#xff0c;简单工厂模式也被称为静态工厂模式。 &#xff5e; 本篇内容包括&#xf…

若依(Ruoyi-Vue-Plus版)——1.登录(SaToken)

这里学习一下若依框架的一个扩展版本&#xff1a; RuoYi-Vue-Plus: 后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-TokenMybatis-PlusJacksonXxl-JobSpringDocHutoolOSS 定期同步 (gitee.com) 官方文档&#xff1a;文档预览 - Gitee.com 项目有关SaToken登录写在最后&#xff0c…

论文阅读笔记 | 三维目标检测——SECOND算法

如有错误&#xff0c;恳请指出。 文章目录1. 背景2 网络结构2.1 3d Backbone2.2 Loss Compute2.3 Data Augment3. 实验结果paper&#xff1a;《SECOND: Sparsely Embedded Convolutional Detection》 1. 背景 鉴于VoxelNet等3d检测算法中使用的3d卷积会导致计算量大且推理速度…

智慧税务解决方案-最新全套文件

智慧税务解决方案-最新全套文件一、建设背景税务大数据现状和挑战1、数据割裂&#xff0c;外部数据整合不足&#xff0c;智能化应用不足2、缺乏统一治理&#xff0c;数据资产不清&#xff0c;质量不高3、数据获取效率低&#xff0c;数据冗余、查询效率低4、 运营运维体系不健全…

【Linux】---进程地址空间

文章目录什么是进程地址写时拷贝为什么要有进程地址为了保护物理内存更方便进行进程和进程数据的解耦统一视角操作系统怎么管理进程地址区域划分在之前学习C/C的时候都会提到 地址这个概念&#xff0c;我们写代码时创建变量&#xff0c;定义函数等都会有其对应的地址空间。而地…

成为数字游民,他们为何「All in Web3」?

成为数字游民&#xff0c;他们为何「All in Web3」&#xff1f; “早上好&#xff0c;夜之城。” 赛博朋克承载着一代人对未来世界的遐想。今年上映的「赛博朋克&#xff1a;边缘行者」中&#xff0c;主人公大卫面临着不夜城旧有制度的高压和控制&#xff0c;对旧有秩序不断发…

C++STL-string类的实现(下)

文章目录1. 流插入和流提取1.1 流插入1.2 流提取2. 现代写法的拷贝构造和赋值函数2.1 现代写法的拷贝构造2.1 现代写法的赋值函数3. string类的迭代器实现3.1 正向迭代器1. 流插入和流提取 1.1 流插入 可能有的同学会这样去写&#xff1a;以一个字符串形式来输出。还有的人会这…

《CTFshow - Web入门》03. Web 21~30

Web 21~30web21知识点题解web22知识点题解web23知识点题解web24知识点题解web25知识点题解web26知识点题解web27知识点题解web28知识点题解web29知识点题解web30知识点题解web21 知识点 tomcat 认证爆破burpsuite暴力破解&#xff08;Sniper&#xff09;之custom iterator使用…