我有一个带有图片供稿的应用程序(Instagram风格)。 我试图通过长时间单击图像来显示快速图像预览。
主要思想是,当用户进行长按时,在对话框中显示图像,然后在用户上下移动手指时修改缩放,并在释放手指时关闭预览。
要存档,我在片段的适配器中有一个onLongClick,如下所示:
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
listener.onLongClick(item.getId());
return false;
}
});
然后,该片段实现侦听器并按如下所示调用对话框:
@Override
public void onLongClick(long itemId) {
FullscreenPhotoPreviewDialog dialog = FullscreenPhotoPreviewDialog.newInstance(itemId);
dialog.show(getActivity().getSupportFragmentManager(), "FullscreenPhotoPreviewDialog");
}
最后,对话框实现了所有的OnTouch逻辑,以使用户无需松开手指即可进行缩放。
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch(motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
float scale = 0;
if (motionEvent.getHistorySize() > 0)
scale = ((motionEvent.getY() > motionEvent.getHistoricalY(motionEvent.getHistorySize() - 1)) ? 0.1f : -0.1f);
FullscreenPhotoPreviewDialog.this.applyScale(scale);
break;
case MotionEvent.ACTION_UP:
FullscreenPhotoPreviewDialog.this.dismiss();
break;
}
return true;
}
});
}
长按打开对话框的流程正常。 问题在于onTouch。长按不会将ACTION_DOWN事件发送到onTouch。所以我需要拉起,然后再次拉下以启动onTouch。
有什么方法可以做到这一点?要长按自动调用ACTION_DOWN?
谢谢你,对不起我的英语!
答案 0 :(得分:0)
使用itemView onTouch事件在第一次触摸后第一次释放之前检测手指的动作。释放后,下一个onTouch事件将发送到对话框,如果用户触摸该对话框,则将调用该对话框的ACTION_DOWN。
在用户保持触摸状态下,触摸动作将由同一视图跟踪。进入或越过另一个视图区域都没关系。
class SomeClass {
private int touchCount;
private ItemFunctions itemFunctions;
private FullscreenPhotoPreviewDialog dialog;
void someFunction(Holder holder) {
holder.itemView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// This block is called after first touch and before releasing his finger
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
// First touch down event here.
// Second one depends on where user will touch again.
if (0 >= touchCount) {
itemFunctions = new ItemFunctions(item.getId());
}
touchCount++;
break;
}
case MotionEvent.ACTION_MOVE: {
// First touch move events here
float scale = 0;
...
if (itemFunctions.displayed) {
itemFunctions.applyScale(scale);
}
break;
}
case MotionEvent.ACTION_UP: {
// First up event here.
// Second one depends on where user will touch again.
break;
}
}
// This should be false to get long touch event
return false;
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
// dialog may need itemFunctions to manipulate zoom, dismiss state callback, etc.
dialog = FullscreenPhotoPreviewDialog.newInstance(itemFunctions.itemId);
dialog.show(getActivity().getSupportFragmentManager(), "FullscreenPhotoPreviewDialog");
itemFunctions.displayed = true;
return false;
}
});
}
}
class ItemFunctions {
long itemId;
boolean displayed;
ItemFunctions(long itemId) {
this.itemId = itemId;
}
void applyScale(float scale) {
}
}
附加说明
如果UI或与触摸相关的功能花费很长时间,则可能会阻止其他事件。在这种情况下,Handler很有用。
没有处理程序
void funcA() {
// funcB wiil be executed inside funcA.
funcB();
}
有处理程序
void funcA() {
// Send a request to call funcB after this point.
// Then looper will fetch the request from queue.
// And funcB will be called then.
// handler.post() -> sendMessageDelayed(getPostMessage(), ...)
// getPostMessage() -> Message m = Message.obtain(); ... return m;
handler.post(new Runnable() {
@Override
public void run() {
funcB();
}
});
}
void funcB() {
}
要了解为什么会阻止事件,我们应该知道输入事件是如何传递和处理的。
如果您熟悉Windows消息泵,请将ViewRootHandler.handleMessage()与Window Procedure和Looper.loop()与Message Loop进行比较。
Android上的Looper和Windows上的消息泵具有相似的概念。他们传递排队的消息。输入事件(例如触摸,单击等)就是此类消息之一。因此,如果某些功能阻塞或需要很长时间来处理一条消息,则其他消息将等待下一个队列提取。
Android上的处理器和Windows程序上的窗口过程用于处理此类消息。解码发送的内容并执行相应的任务。
Android上的ViewRootImpl.ViewRootHandler.handleMessage
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_PROCESS_INPUT_EVENTS:
mProcessInputEventsScheduled = false;
doProcessInputEvents();
break;
...
}
}
Windows程序中的窗口过程
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
...
case WM_MOUSEMOVE:
return 0;
...
}
}
Android上的Looper.loop
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
...
try {
msg.target.dispatchMessage(msg);
...
} finally {
...
}
}
}
Windows程序中的消息循环
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
参考
What is a message pump?(Windows)