Android自定义万能Canvas画布
一、需求:
1.在自定义的画布中实现可缩放手势,摇一摇可对控件进行整理排序;
2.画布中可以添加位置设定的控件,控件可以响应点击、长按、拖动事件;
3.控件A长按事件会隐藏画布中的控件除了A之外,显示另一个控件B;当A在在底层画布中拖动,拖动结束之后回到原画布;当A移动B的位置范围响应操作(可以添加另方面功能)。
二、实现思想:
1、画布的的手势缩放、控件的添加,在我的上一篇关于画布文章中已经实现了这个功能,这里不再赘述;
2、要实现上述的几个功能只需要屏幕上添加两层画布,一层画布用于添加控件在这层中可以实现控件的点击、拖动、画布缩放、长按事件、整理排序控件。底层画布用于长按其他控件隐藏之后A控件的拖动和B控件的显示及A拖动到B之后的事件响应。
3、当A控件结束拖动(抬起时)回到第一层画布中。
三、效果展示:
四、具体实现:
1.先添加两层画布用布局可以RelativeLayout包裹着,如:
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <com.view.ActionEditorCanvasView
- android:id="@+id/action_editor_canvas_gamepad_test"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- <com.view.ActionEditorCanvasView
- android:id="@+id/action_editor_canvas_test"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- </RelativeLayout>
2.当控件添加到画布中要获取到对应控件的位置信息(将添加的控件添加到一个集合中),判断点击时是否是落在控件之上,这些都是在view中的onTouchEvent(MotionEvent event)进行处理:
- private int getDown2Widget() {
- for (int i = 0; i < mDrawableList.size(); i++) {
- int xcoords = mDrawableList.get(i).getXcoords();
- int ycoords = mDrawableList.get(i).getYcoords();
- double abs = Math.sqrt((DownX - xcoords) * (DownX - xcoords) + (DownY - ycoords) * (DownY - ycoords));
- //点落在控件内
- if (abs < ActionWidget.RADIUS) {
- return i;
- }
- }
- return -1;
- }
3、在画布中实现Move、LongPress、Up、Click的接口回调用于对外应用:
- public onWidgetUpListener mOnWidgetUpListener;
- public interface onWidgetUpListener{
- void onWidgetUp(int index,int x,int y);
- }
- public void setOnWidgetUpListener(onWidgetUpListener mOnWidgetUpListener){
- this.mOnWidgetUpListener=mOnWidgetUpListener;
- }
- public onWidgetMoveListener mOnWidgetMoveListener;
- public interface onWidgetMoveListener{
- void onWidgetMove(int index,int x,int y);
- }
- public void setOnWidgetMoveListener(onWidgetMoveListener moveListener){
- this.mOnWidgetMoveListener=moveListener;
- }
- public onWidgetLongPressListener mOnWidgetLongPressListener;
- public interface onWidgetLongPressListener{
- void onWidgetLongPress(int index,int x,int y);
- }
- public void setOnWidgetLongPressListener(onWidgetLongPressListener mOnWidgetLongPressListener){
- this.mOnWidgetLongPressListener=mOnWidgetLongPressListener;
- }
- public onWidgetClickListener mOnWidgetClickListener;
- public interface onWidgetClickListener{
- void onWidgetClick(int index,int x,int y);
- }
- public void setOnWidgetClickListener(onWidgetClickListener mOnWidgetClickListener){
- this.mOnWidgetClickListener=mOnWidgetClickListener;
- }
4.接下来就是处理拖动、点击、长按、抬起的事件的处理:
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- mDownTime = System.currentTimeMillis();
- DownX = event.getX();//float DownX
- DownY = event.getY();//float DownY
- //判断点击的坐标范围是否在控件上
- mDown2Widget = getDown2Widget();
- moveX = 0;
- moveY = 0;
- moveX1 = 0;
- moveY1 = 0;
- }
- break;
- case MotionEvent.ACTION_MOVE: {
- moveX += Math.abs(event.getX() - DownX);//X轴距离
- moveY += Math.abs(event.getY() - DownY);//y轴距离
- moveX1 = event.getX();
- moveY1 = event.getY();
- if (moveX == 0 && moveY == 0) {
- mMoveTime = System.currentTimeMillis();
- long DValueTime = mMoveTime - mDownTime;//计算点击下去是否有移动及事件是否符合长按的时间值,这样可以判断是否是长按事件
- if (DValueTime>200){<span style="white-space:pre"> </span>
- if (mOnWidgetLongPressListener!=null){
- mOnWidgetLongPressListener.onWidgetLongPress(mDown2Widget,(int)moveX1,(int)moveY1);
- }
- }
- return true;
- } else {
- if (mDown2Widget > -1) {
- if (mOnWidgetMoveListener!=null){
- mOnWidgetMoveListener.onWidgetMove(mDown2Widget,(int)moveX1,(int)moveY1);
- }
- mDrawableList.get(mDown2Widget).setXcoords((int) moveX1);//点击在控件之上进行的move则把控件坐标值重置,从而是实现控件拖动
- mDrawableList.get(mDown2Widget).setYcoords((int) moveY1);
- invalidate();
- }
- }
- DownX = event.getX();
- DownY = event.getY();
- }
- break;
- case MotionEvent.ACTION_UP: {
- long moveTime = System.currentTimeMillis() - currentMS;//移动时间
- mUpTime = System.currentTimeMillis();
- long DValueTime = mUpTime - mDownTime;//判断从按下到抬起的实现,从而实现判断是否是点击
- if (mDown2Widget > -1) {
- //判断是否为拖动事件
- if (!(moveTime > 1000 && (moveX > 100 || moveY > 100))) {
- if (DValueTime < 200) {
- if (mOnWidgetClickListener!=null){
- mOnWidgetClickListener.onWidgetClick(mDown2Widget,(int)moveX1,(int)moveY1);
- }
- }
- }
- }
- if (mOnWidgetUpListener!=null){//判断是否是抬起事件
- mOnWidgetUpListener.onWidgetUp(mDown2Widget,(int)moveX1,(int)moveY1);
- }
- }
- break;
- }
- return true;
- }
5、在底层画布添加控件B,并获取位置信息存起来:
- mBitmap= BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
- mGamePadBitmap=new CBitmap(mBitmap,200,1000);
- mXcoords = mGamePadBitmap.getXcoords();
- mYcoords = mGamePadBitmap.getYcoords();
- mGamePadCanvasView.addCanvasDrawable(mGamePadBitmap);
6、处理长按事件,隐藏第一层画布显示底层画布,并获取A控件位置在底层画布中画出来:
- mCanvasView.setOnWidgetLongPressListener(new ActionEditorCanvasView.onWidgetLongPressListener() {
- @Override
- public void onWidgetLongPress(int index, int x, int y) {
- ActionWidget actionWidget = (ActionWidget) mCanvasView.mDrawableList.get(index);
- mCanvasView.setVisibility(View.GONE);
- mGamePadCanvasView.setVisibility(View.VISIBLE);
- mGamePadWidget=new ActionWidget(x, y, mPaint);
- mGamePadCanvasView.addCanvasDrawable(mGamePadWidget);
- isGamePadCanvas=true;//把是否显示底层画布的开关开启
- }
- });
7、判断A控件是否移动B控件的位置范围之上:
- mCanvasView.setOnWidgetMoveListener(new ActionEditorCanvasView.onWidgetMoveListener() {
- @Override
- public void onWidgetMove(int index, int x, int y) {
- if (isGamePadCanvas){
- if (mGamePadWidget!=null){
- mGamePadCanvasView.mDrawableList.get(1).setXcoords(x);
- mGamePadCanvasView.mDrawableList.get(1).setYcoords(y);
- mGamePadCanvasView.invalidate();
- if ((x>mXcoords&&x<mXcoords+250)&&(y>mYcoords&&y<mYcoords+250)){
- Toast.makeText(ActionCanvasTestActivity.this, "控件移动到控制器按钮界面!!!!!" , Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
- });
8、最后是判断抬起事件,如底层画布是显示则隐藏底层画布显示第一层画布:
- mCanvasView.setOnWidgetUpListener(new ActionEditorCanvasView.onWidgetUpListener() {
- @Override
- public void onWidgetUp(int index, int x, int y) {
- if (isGamePadCanvas){
- mCanvasView.setVisibility(View.VISIBLE);
- mGamePadCanvasView.setVisibility(View.GONE);
- mGamePadCanvasView.mDrawableList.remove(1);
- isGamePadCanvas=false;
- }
- }
- });
五、Demo项目地址:http://download.csdn.net/download/wangyongyao1989/9901019
Android自定义万能Canvas画布的更多相关文章
- 【读书笔记《Android游戏编程之从零开始》】12.游戏开发基础(Canvas 画布)
1.Canvas 画布 画布类 Canvas 封装了图形和图片绘制等内容,此类常用的函数说明如下: drawColor(int color) 作用:绘制颜色覆盖画布,常用于刷屏 参数:颜色值,也可用十 ...
- Android自定义组件系列【9】——Canvas绘制折线图
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了很多插件,但是很多时候我们需要根据具体项目自定义这些图表,这一篇文章我们一起来看看如何在Android中使用Canvas ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
- Android 自定义view(二) —— attr 使用
前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...
- Android 自定义View之BounceProgressBar
之前几天下载了很久没用了的桌面版酷狗来用用的时候,发现其中加载歌曲的等待进度条的效果不错(个人感觉),如下: 然后趁着这周末两天天气较冷,窝在宿舍放下成堆的操作系统作业(目测要抄一节多课的一堆堆文字了 ...
- Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...
- Android自定义视图二:如何绘制内容
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- Android 自定义View之自绘控件
首先要提前声明一下,我对于自定义View的理解并不是很深,最近啃了几天guolin博主写的关于自定义View的博客,讲的非常棒,只不过涉及到源码和底层的一些东西,我自己就懵逼了,目前只是会了关于自定义 ...
- android 自定义view 前的基础知识
本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...
随机推荐
- HQL知识点一
Hive创表语法 create [external] table [if not exists] [db_name.]table_name (col1_name data_type,col2_name ...
- Ubuntu系统下Anaconda使用方法总结
前言:个人认为Anaconda比pip使用起来要方便很多,因为它创建虚拟环境和下载各种库都很方便.但是有时候conda的安装方法中没有某些库,必须使用pip来进行安装.所以,主要用Anaconda,偶 ...
- 番外篇1:在Windows环境中安装JDK
他山之石,可以攻玉!欢迎关注我的微信公众号 本文作为构建第一个Java程序的番外篇一,跟大家探讨下在Windows下怎么安装JDK.由于本人没有Mac,因此如果是Mac的同学,请自行百度哦! 读前预览 ...
- spring源码1:基本概念
一.预习 1.如何用spring?零配置(注解)或少配置,与应用无侵入性一起运行,与主流框架无缝集成. 2.spring 是什么?spring 是 java 企业应用级框架,目的是为了简化开发:主要体 ...
- Spring+Mybatis+SpringMVC+Maven+MySql搭建实例(转)
http://blog.csdn.net/evankaka/article/details/48785513?spm=5176.100239.blogcont28591.10.9Fdj9R
- CentOS官网下载系统镜像
https://jingyan.baidu.com/article/1876c85279cedd890a13766c.html
- mock数据,尽量随机,采用中间表的方式实现
开发平台上的sql不能超过1000行,而为了插入尽可能随机的数据,sql比较长---插入一行数据就需要执行80行sql,因此执行insert into mall_data.dtw_mall2_tmp ...
- 对于"单链表逆置和递归"的问题的理解.
一. 相关知识要点: 学习或了解基础数据结构和C语言, 对基础链表知识或相关知识有概况性认识. 例如: 本题目结构为: #define elem_type int typedef struct _si ...
- Spring(转载二)
在网上看到一篇文章,感觉写得挺不错的,转载一下,本文转载自:http://blog.csdn.net/m13666368773/article/details/7802126 一. IoC理论的背景 ...
- 窗函数法设计FIR滤波器参数特征表