2024年Android界面导航之拖放框架(Drag and Drop)(1),2024年最新2024BAT大厂Android社招面试题

如果你进阶的路上缺乏方向,可以加入我们的圈子和安卓开发者们一起学习交流!

  • Android进阶学习全套手册

    img

  • Android对标阿里P7学习视频

    img

  • BATJ大厂Android高频面试题

    img

最后,借用我最喜欢的乔布斯语录,作为本文的结尾:

人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

如果释放动作顺利,监听器应该返回true,否则应该返回false。

|- style=“vertical-align:top;”

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;" |

ACTION_DRAG_ENDED

| style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px;” |

当系统结束拖动动作时,视图对象的拖动事件监听器就会接收到这个类型的拖动事件。这种操作类型不一定是前面有一个ACTION_DROP事件。如果系统发送一个ACTION_DROP,并接收到一个ACTION_DRAG_ENDED操作类型,并不意味着拖动事件的成功。监听器必须调用getResult())方法来获取在回应 ACTION_DROP事件中返回的结果。如果ACTION_DROP事件没有被发送,那么getResult())就返回false。

|}

表2.

{|style="border-spacing: 0px;margin: 4px 4px; width: 90%; border-left:1px solid #ccc;border-top:1px solid #ccc; "

|-style="background:#DEE8F1; "

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getAction()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getClipDescription()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getLocalState()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getX()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getY()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getClipData()的值

! style=“border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px” | getResult()的值

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DRAG_STARTED

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DRAG_ENTERED

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DRAG_LOCATION

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DRAG_EXITED

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DROP

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

|- style=" vertical-align:top;"

| style=" border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

ACTION_DRAG_ENDED

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

| style="border-right:1px solid #ccc;border-bottom:1px solid #ccc; padding:5px 15px; " |

X

|}

如果一个方法不包含对某个特定的操作类型有效的数据,那么就会根据该方法的返回值类型返回null或0.

拖动阴影


在拖动过程中,系统会显示一张用户拖动的图片。对数据移动而言,这张图片代表着那些正在被移动的数据。对其他操作而言,这张图片代表着拖动操作的某些环节。

这张图片就被叫做是一个拖动阴影。你可以通过你声明的View.DragShadowBuilder对象的方法去创建它,然后当你使用startDrag())方法的一部分,系统调用你定义的View.DragShadowBuilder里面的回调方法去获取一个拖动阴影。

View.DragShadowBuilder类有两个构造函数:

View.DragShadowBuilder(View))

::这个构造函数接受你的应用程序中的任意一个视图对象。它将视图对象存储View.DragShadowBuilder对象中,因此在回调过程中,你可以去访问这个构造方法,为你构造一个拖动阴影。构造方法不必和用户选择开始一个拖动的视图对象(如果有的话)相关联。

::如果你使用这个构造方法,不必去继承View.DragShadowBuilder类或覆盖它的方法。默认情况下,你会得到一个与你作为参数传递的那个视图有相同外表的拖动阴影,并且该拖动阴影会居中位于用户接触的屏幕上。

View.DragShadowBuilder())

::如果你使用这个构造方法,在View.DragShadowBuilder 对象中没有一个视图对象是有效的(这个字段被设置为null)。如果使用该构造函数,你不必继承View.DragShadowBuilder类或覆盖它的方法,你可以得到一个不可见的的拖动阴影。系统不会给出一个错误。

View.DragShadowBuilder类有两个方法:

onProvideShadowMetrics())

::系统在调用了android.view.View.DragShadowBuilder,java.lang.Object,int) startDrag()这个方法之后,立刻回调用这个方法。用这个方法给系统发送拖动阴影的规模和接触点。这个方法有两个参数:

:dimensions

::一个Point对象。拖动阴影的宽为x,高为y

:touch_point

::一个Point对象。这个接触点应该是拖动过程中,在用户手指之下的拖动阴影的位置。它的X轴坐标为x,Y轴坐标为y

onDrawShadow())

:在调用了onProvideShadowMetrics())方法之后,系统立刻调用onDrawShadow())这个方法来获取拖动阴影。这个方法只有一个参数,一个Canvas对象,该对象是系统利用你提供给 onProvideShadowMetrics())方法里面的参数构造出来的。利用它可以在提供给你的Canvas对象中绘制你的拖动阴影。

* 设计一个拖放操作*

=============

这个章节会一步一步说明如何开始一个拖动,如何在拖动过程中回应事件,如何回应一个拖动事件以及如何结束一个拖放操作。

开始一个拖动动作


用户用一个拖动的手势开始一个拖动,通常是一个在视图对象上的长按动作。作为回应,应做到以下几点:

  1. 必要时,为那些已经移动的数据创建一个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操作类型的事件包含了对getX())和getY())方法有效的数据,相应的接触点的位置。监听器可能可以使用这些信息来改变在接触点的视图的部分的外观。监听器也可以用这些信息来决定用户想要释放拖动阴影的精确位置。

  • 在回应ACTION_DRAG_EXITED时,监听器应该重置它在回应ACTION_DRAG_ENTEREDACTION_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

最后

下面是辛苦给大家整理的学习路线

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

highlighttable" style=“border-collapse:collapse;border-spacing:0px;border:1px solid #cccccc;color:#333333;font-family:‘微软雅黑’, ‘Helvetica Neue’, Helvetica, Arial, sans-serif;line-height:26px;”>

  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

最后

下面是辛苦给大家整理的学习路线

[外链图片转存中…(img-59NPHIq1-1714980674518)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值