View.DragShadowBuilder类有两个构造函数:
::这个构造函数接受你的应用程序中的任意一个视图对象。它将视图对象存储View.DragShadowBuilder对象中,因此在回调过程中,你可以去访问这个构造方法,为你构造一个拖动阴影。构造方法不必和用户选择开始一个拖动的视图对象(如果有的话)相关联。
::如果你使用这个构造方法,不必去继承View.DragShadowBuilder类或覆盖它的方法。默认情况下,你会得到一个与你作为参数传递的那个视图有相同外表的拖动阴影,并且该拖动阴影会居中位于用户接触的屏幕上。
::如果你使用这个构造方法,在View.DragShadowBuilder 对象中没有一个视图对象是有效的(这个字段被设置为null)。如果使用该构造函数,你不必继承View.DragShadowBuilder类或覆盖它的方法,你可以得到一个不可见的的拖动阴影。系统不会给出一个错误。
View.DragShadowBuilder类有两个方法:
::系统在调用了android.view.View.DragShadowBuilder,java.lang.Object,int) startDrag()这个方法之后,立刻回调用这个方法。用这个方法给系统发送拖动阴影的规模和接触点。这个方法有两个参数:
:dimensions
:touch_point
::一个Point对象。这个接触点应该是拖动过程中,在用户手指之下的拖动阴影的位置。它的X轴坐标为x,Y轴坐标为y。
:在调用了onProvideShadowMetrics())方法之后,系统立刻调用onDrawShadow())这个方法来获取拖动阴影。这个方法只有一个参数,一个Canvas对象,该对象是系统利用你提供给 onProvideShadowMetrics())方法里面的参数构造出来的。利用它可以在提供给你的Canvas对象中绘制你的拖动阴影。
* 设计一个拖放操作*
=============
这个章节会一步一步说明如何开始一个拖动,如何在拖动过程中回应事件,如何回应一个拖动事件以及如何结束一个拖放操作。
开始一个拖动动作
用户用一个拖动的手势开始一个拖动,通常是一个在视图对象上的长按动作。作为回应,应做到以下几点:
- 必要时,为那些已经移动的数据创建一个ClipData 和ClipData.Item对象。作为ClipData这个对象的一部分,在ClipData中提供存储在ClipDescription对象中的元数据。因为一个拖放动作不能代表数据的移动,你可能想要使用null来代替一个实际的数据。
比如:下面这个代码片段说明了如何通过创建一个包含了ImageView的标志或标签的ClipData对象,来回应在ImageView上的一个长按动作。以下就是这些片段,第二个片段说明了如何重写View.DragShadowBuilder这个类中的方法。
// Create a string for the ImageView label
private static final String IMAGEVIEW_TAG = “icon bitmap”
// Creates a new ImageView
ImageView imageView = new ImageView(this);
// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)
imageView.setImageBitmap(mIconBitmap);
// Sets the tag
imageView.setTag(IMAGEVIEW_TAG);
1 |
...
|
// Sets a long click listener for the ImageView using an anonymous listener object that
// implements the OnLongClickListener interface
imageView.setOnLongClickListener(new View.OnLongClickListener() {
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Defines the one method for the interface, which is called when the View is long-clicked
public boolean onLongClick(View v) {
// Create a new ClipData.
// This is done in two steps to provide clarity. The convenience method
// ClipData.newPlainText() can create a plain text ClipData in one step.
// Create a new ClipData.Item from the ImageView object’s tag
ClipData.Item item = new ClipData.Item(v.getTag());
// Create a new ClipData using the tag as a label, the plain text MIME type, and
// the already-created item. This will create a new ClipDescription object within the
// ClipData, and set its MIME type entry to “text/plain”
ClipData dragData = new ClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);
// Instantiates the drag shadow builder.
View.DrawShadowBuilder myShadow = new MyDragShadowBuilder(imageView);
// Starts the drag
v.startDrag(dragData, // the data to be dragged
myShadow, // the drag shadow builder
null, // no need to use local data
0 // flags (not currently used, set to 0)
);
}
}
2.下面这个代码片段定义了myDragShadowBuilder 。它为拖动一个TextView创建了一个小的灰色矩形框拖动阴影。
private static class MyDragShadowBuilder extends View.DragShadowBuilder {
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// The drag shadow image, defined as a drawable thing
private static Drawable shadow;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) {
// Stores the View parameter passed to myDragShadowBuilder.
super(v);
// Creates a draggable image that will fill the Canvas provided by the system.
shadow = new ColorDrawable(Color.LTGRAY);
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
@Override
public void onProvideShadowMetrics (Point size, Point touch)
// Defines local variables
private int width, height;
// Sets the width of the shadow to half the width of the original View
width = getView().getWidth() / 2;
// Sets the height of the shadow to half the height of the original View
height = getView().getHeight() / 2;
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height);
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
@Override
public void onDrawShadow(Canvas canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas);
}
}
* 注意* :记住你不必去继承View.DragShadowBuilder。构造方法View.DragShadowBuilder(View))会创建一个默认的拖动阴影,这个拖动阴影与传递给它的View参数一样大,并且位于以接触点为中心的位置。
回应一个拖动的开始
在拖动过程中,系统将拖动事件分配给当前布局中的视图对象的拖动事件监听器。监听器应该调用getAction())这个方法获取操作类型。在一个拖动开始时,这个方法返回ACTION_DRAG_STARTED。
作为回应一个操作类型为 ACTION_DRAG_STARTED的事件,监听器应该做到以下几点:
1.调用getClipDescription())方法获取ClipDescription。使用在ClipDescription 中的MIME类型的方法查看监听器是否接收被拖动的数据。
如果拖放操作没有代表数据的移动,那么这个步骤就不是必须的。
2.如果监听器可以接收一个拖动,它必须返回true。这样会告诉系统继续发送拖动事件给监听器。如果监听器不接收一个拖动,就会返回false,系统就会停止发送拖动事件直到它发送ACTION_DRAG_ENDED。
注意对于ACTION_DRAG_STARTED事件,以下这些DragEvent的方法都是无效的:getClipData())、 getX())、 getY())和getResult())。
在拖动过程中处理事件
在拖动过程中,作为回应ACTION_DRAG_STARTED拖动事件,监听器返回true来继续接受拖动事件。监听器在拖动过程中接收到的拖动事件类型取决于拖放阴影的位置以及监听器视图的可见性。
在拖动过程中,监听器首先使用拖动事件来决定是否应该改变他们的视图的外观。
在拖动过程中,getAction())返回以下三个变量中的一个:
-
ACTION_DRAG_ENTERED:当接触点(屏幕上位于用户手指下的那个点)进入监听器的视图的边界框范围内时监听器会接收到这个事件。
-
ACTION_DRAG_LOCATION:一旦监听器接收到ACTION_DRAG_LOCATION事件,在它接收到ACTION_DRAG_EXITED事件之前,接触点每移动一次,它都会接收到一个新的ACTION_DRAG_LOCATION事件。方法getX())和getY())会返回接触点的X轴和Y轴的坐标。
-
ACTION_DRAG_EXITED:在拖动阴影不再位于监听器视图的边界框范围之内时,这个事件会被发送给以前接收到ACTION_DRAG_ENTERED事件的监听器。
监听器不必对这些操作类型中的任意一个作出反应。如果监听器返回一个值给系统,它会被忽略掉。下面是应对这些动作类型的一些准则:
-
在回应ACTION_DRAG_ENTERED或者ACTION_DRAG_LOCATION时,监听器可以通过改变视图的外观来表明它将要接收到一个拖动。
-
具有ACTION_DRAG_LOCATION操作类型的事件包含了对getX())和getY())方法有效的数据,相应的接触点的位置。监听器可能可以使用这些信息来改变在接触点的视图的部分的外观。监听器也可以用这些信息来决定用户想要释放拖动阴影的精确位置。
-
在回应ACTION_DRAG_EXITED时,监听器应该重置它在回应ACTION_DRAG_ENTERED或ACTION_DRAG_LOCATION中应用的任何外观的变化。这是在向用户表明视图不再是一个临近被释放的目标。
回应一个释放动作
当用户在应用程序的视图上释放拖动阴影时,并且该视图会事先报告是否可以接收被拖动的内容,系统将拖动事件分发给那个含有 ACTION_DROP操作类型的视图。监听器应做到以下几点:
1.调用getClipData())方法获取最初在startDrag())方法中应用的ClipData对象,并储存之。如果拖放操作没有代表数据的移动,这些都不是必须的。
2.监听器应返回true来表明释放动作已顺利完成,如果没有完成的话,则返回false。这个被返回的值成为ACTION_DRAG_ENDED事件中getResult())方法的返回值。
需要注意的是,如果系统没有发送出ACTION_DROP事件,那么ACTION_DRAG_ENDED事件中getResult())方法的返回值就为false。
对于ACTION_DROP事件来说,在释放动作的瞬间,getX())和getY())方法使用接收释放动作的视图上的坐标系统,返回拖动点的X轴和Y轴的坐标。
系统允许用户在监听器不接收拖动事件的视图上释放拖动阴影。系统允许用户在应用程序UI的空区域或者应用程序之外的区域释放拖动阴影。在以上例子中,系统虽然会发送ACTION_DRAG_ENDED事件,但是不会发送一个ACTION_DROP事件。
回应一个拖动的结束
用户释放了拖动阴影后,系统会立即给应用程序中所有的拖动事件监听器发送ACTION_DRAG_ENDED类型的拖动事件,表明拖动动作结束了。
每个监听器都应该做下列事情:
1.如果监听器在操作期间改变了View对象的外观,那么应该把View对象重置为默认的外观。这是对用户可见的操作结束的指示.
2.监听器能够可选的调用getResult())方法来查找更多的相关操作。如果在响应ACTION_DROP类型的事件中监听器返回了true,那么getResult())方法也会返回true。在其他的情况中,getResult())方法会返回false,包括系统没有发出ACTION_DROP事件的情况.
3.监听器应该给系统返回true。
回应拖动事件:一个例子
所有的拖动事件都会被拖动事件的回调方法或监听器所接收。以下代码片段是一个简单的在监听器中对拖动事件作出反应的示例。
// Creates a new drag event listener
mDragListen = new myDragEventListener();
View imageView = new ImageView(this);
// Sets the drag event listener for the View
imageView.setOnDragListener(mDragListen);
…
protected class myDragEventListener implements View.OnDragEventListener {
1 |
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
技术进阶之路很漫长,一起共勉吧~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
22013270)]
总结
最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-M9KpRVkq-1712122013270)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
技术进阶之路很漫长,一起共勉吧~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算