博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
安卓权威编程指南-笔记 (第29章定制视图与触摸事件)
阅读量:4581 次
发布时间:2019-06-09

本文共 4539 字,大约阅读时间需要 15 分钟。

1.定制视图

  Android自带众多优秀的标准视图与组件,但有时为追求独特的应用视觉效果,我们仍需创建定制视图。

  定制视图分为两大类别:

  •   简单视图: 简单视图内部也可以很复杂,之所以归为简单类别,是因为简单视图不包括子视图,而且简单视图几乎总是会执行定制绘制。
  •   聚合视图:聚合视图由其他视图对象组成,聚合视图通常管理着子视图,但不负责执行定制绘制,图形绘制任务都委托给了各个子视图。

  

  创建定制视图的所需的三大步骤:

  •   选择超类。对于简单定制视图而言,View是个空白画布,因此它作为超类最常见,对于聚合定制视图,我们应选择合适的超类布局,比如FrameLayout.
  •   继承选定的超类,并至少覆盖一个超类构造方法。
  •   覆盖其他关键方法,以定制视图行为。

1.1 创建一个简单的视图类

public class BoxDrawingView extends View {    // Used when creating the view in code    public BoxDrawingView(Context context) {        this(context, null);    }    // Used when inflating the view from XML    public BoxDrawingView(Context context, AttributeSet attrs) {        super(context, attrs);    }}

  从布局文件中实例化的视图可收到一个AttributeSet实例,该实例包含了XML布局文件中指定的XML属性。即使不打算使用构造方法,按习惯做法也应添加他们。

    有了定制视图类,可以在布局文件里面引用它。

  在引用时必须使用自定义View的全路径名,这样布局inflater才能够找到它,布局文件inflater解析布局XML文件,并按视图定义创建View实例,如果元素名不是全路径名,布局inflater

会转而在android.view和android.widget包中寻找目标,如果目标视图放置在其他包中,布局inflater将无法找到目标并最终导致应用崩溃。

  因此,对于android.view和android.widger包以外的定制视图类,必须指定它们的全路径名。

 

1.2 处理触摸事件

监听触摸事件的一种方式是使用以下view方法,设置一个触摸事件监听器:

public void setOnTouchListener(View.OnTouchListener l)

不过我们的定制视图是View的子类,因此可走捷径直接覆盖以下View方法:

public boolean onTouchEvent(MotionEvent event)

该方法接收一个MotionEvent类实例,MotionEvent类可用来描述包括位置和动作的触摸事件。动作用于描述事件所处的阶段。

动作常量   动作描述
ACTION_DOWN 手指触摸到屏幕
ACTION_MOVE 手指在屏幕上移动
ACTION_UP    手指离开屏幕
ACTION_CANCEL   父视图拦截了触摸事件

 

 

 

 

 

在onTouchEvent()实现方法中,可使用以下MotionEvent方法查看动作值:

public final int getAction()

 

我们的目的就是在一根手指放下的时候记录下放下的位置,移动时随之变化,放开时固定该矩形框。并且之前画的矩形框数据需要记录下来。 

建立一个实体类,用于表示一个矩形框的定义数据。用来保存原始坐标点(手指的初始位置)和当前坐标点(手指的当前位置):

public class Box {    private PointF mOrigin;    private PointF mCurrent;    public Box(PointF origin) {        mOrigin = origin;        mCurrent = origin;    }    //get、set略}

然后重写onTouchEvent()方法并进行相应操作:

private Box mCurrentBox;private List
mBoxen = new ArrayList<>();@Overridepublic boolean onTouchEvent(MotionEvent event) { // 每次有触摸事件都记录下现在的坐标 PointF current = new PointF(event.getX(), event.getY()); String action = ""; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: action = "ACTION_DOWN"; // 每次按下的时候在列表中中新增一个 Box mCurrentBox = new Box(current); mBoxen.add(mCurrentBox); break; case MotionEvent.ACTION_MOVE: action = "ACTION_MOVE"; if (mCurrentBox != null) { // 移动的时候都要重绘 mCurrentBox.setCurrent(current); invalidate(); } break; case MotionEvent.ACTION_UP: // 抬起的时候不再指向最新的 Box action = "ACTION_UP"; mCurrentBox = null; break; case MotionEvent.ACTION_CANCEL: action = "ACTION_CANCEL"; mCurrentBox = null; break; } Log.i(TAG, action + " at x=" + current.x + ", y=" + current.y);z return true;}

在取消触摸事件或用户手指离开屏幕时,应清空mCurrentBox以结束屏幕绘制,以完成的Box会安全的存储在数组中。

invalidate()方法会强制BoxDrawingView重新绘制自己,这样在拖拽时就能实时看到矩形框。

 

2 onDraw()方法内的图形绘制

  应用启动时,所有视图都处于无效状态(视图还没有绘制到屏幕上),为解决这个问题,Android调用了顶级View视图的draw()方法,这会引起自上而下的链式调用反映。

首先,视图完成自我绘制,然后是子视图的自我绘制,再然后是子视图的子视图的自我绘制,如此调用下去直至继承结构的末端。当继承结构中的所有视图都完成自我绘制后,最顶级

View 视图也就生效了。

  为加入这种绘制,可覆盖以下 View 方法:

    protected void onDraw(Canvas canvas);

  在onTouchEvent()方法中响应ACTION_MOVE动作时,我们调用invalidate()方法再次让BoxDrawingView处于失效状态,这迫使他重新完成自我绘制,并再次调用onDraw()方法。

  

  Canvas和Paint是Android系统的两大绘制类。

  •   Canvas类拥有我们需要的所有绘制操作,其方法可决定在哪里以及绘什么,比如线条、圆形、字词、矩形等。
  •   Paint类决定如何绘制,其方法可指定绘制图形的特征,例如是否填充图形、使用什么字体绘制、线条是什么颜色等

  

public BoxDrawingView(Context context, AttributeSet attrs){ //AttributeSet实例包含了XML布局文件中指定的XML属性。        super(context,attrs);        mBoxPaint = new Paint();        mBoxPaint.setColor(0x22ff0000);        mBackgroundPaint = new Paint();        mBackgroundPaint.setColor(0xfff8efe0);    } @Override    protected void onDraw(Canvas canvas){        //先画出背景        canvas.drawPaint(mBackgroundPaint);        //画出每个绘制过的矩形        for(Box box : mBoxen){            float left = Math.min(box.getOrigin().x, box.getCurrent().x);            float right = Math.max(box.getOrigin().x, box.getCurrent().x);            float top = Math.min(box.getOrigin().y, box.getCurrent().y);            float bottom = Math.max(box.getOrigin().y,box.getCurrent().y);            canvas.drawRect(left, top ,right ,bottom, mBoxPaint);        }    }

 

 以上代码的第一部分简单直接:使用米白背景paint,填充canvas以衬托矩形框。然后,针对矩形框数组中的每一个矩形框,据其两点坐标,确定矩形框上下左右的位置。绘制时,左端和顶端的值作为最小值,右端和底端的值作为最大值。完成位置坐标值计算后,调用 Canvas.drawRect(...) 方法,在屏幕上绘制红色矩形框。

 

 

   

  

转载于:https://www.cnblogs.com/chase1/p/7225877.html

你可能感兴趣的文章
2019.7.4 打卡第五天
查看>>
day06数据类型----元组、字典、集合
查看>>
while循环(break、continue)
查看>>
Docker: docker container常用命令实战(2)-数据持久化
查看>>
Yahoo!网站性能最佳体验的34条黄金守则——服务器
查看>>
国内4G频段划分
查看>>
20120104登陆与改密码
查看>>
How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
查看>>
UITableViewCell背景色.选中背景色,分割线,字体颜色设置
查看>>
MyBatis笔记一:GettingStart
查看>>
查找不同的木棍
查看>>
面试题:顺时针打印矩阵
查看>>
DataSet、DataTable、DataRow、DataColumn区别及使用实例
查看>>
python 特殊方法
查看>>
Python3 练习笔记四
查看>>
装箱问题
查看>>
Android线程管理(一)——线程通信
查看>>
vim 使用技巧
查看>>
Periodic String UVa1225
查看>>
Android 演示 DownloadManager——Android 下载 apk 包并安装
查看>>