- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
这是我正在尝试做的事情的图片:
Image: Drag-and-drop app with a list and an item outside the list
我正在尝试制作一个类似资源管理器的文件浏览器,通过拖放来移动文件,但我遇到了一个问题。
我知道有一个特殊的 RecyclerView 拖放界面(例如有 this ),但我没能找到说明如何在内部和 之间移动事物的示例在列表之外。
这是我的 XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="net.wbord.recyclerviewdragdropautoscrolltest.MainActivity">
<TextView
android:id="@+id/exampleItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Hello World!"/>
<Space
android:layout_width="match_parent"
android:layout_height="50dp"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/mainList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingLeft="100dp"
android:paddingRight="100dp"
android:clipToPadding="false"
/>
</LinearLayout>
还有 Java:
package net.wbord.recyclerviewdragdropautoscrolltest;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
// Find Views:
final TextView exampleItem = (TextView) findViewById (R.id.exampleItem);
final RecyclerView rv = (RecyclerView) findViewById (R.id.mainList);
// Define Drag Listener:
final View.OnDragListener onDrag = new View.OnDragListener () {
@Override
public boolean onDrag (View v, DragEvent event) {
switch (event.getAction ()) {
case DragEvent.ACTION_DRAG_ENTERED:
v.setScaleX (1.5f); v.setScaleY (1.5f);
handleScroll (rv, v);
break;
case DragEvent.ACTION_DRAG_EXITED:
case DragEvent.ACTION_DRAG_ENDED:
v.setScaleX (1); v.setScaleY (1);
break;
case DragEvent.ACTION_DROP:
ClipData data = event.getClipData ();
String folder = (String) v.getTag ();
String msg = "File '" + data.getItemAt (0).getText () + "' " +
"moved into folder '" + folder + "'";
Toast.makeText (MainActivity.this, msg, Toast.LENGTH_LONG).show ();
break;
}
return true;
}
};
// The "file" for the user to drag-drop into a folder:
exampleItem.setOnLongClickListener (new View.OnLongClickListener () {
@Override
public boolean onLongClick (View v) {
// Start drag:
ClipData.Item item = new ClipData.Item (exampleItem.getText ());
ClipData data = new ClipData (exampleItem.getText (),
new String [] {ClipDescription.MIMETYPE_TEXT_PLAIN},
item);
View.DragShadowBuilder builder = new View.DragShadowBuilder (exampleItem);
v.startDrag (data, builder, null, 0);
return true;
}
});
// The list of "folders" that can accept the file:
rv.setLayoutManager (new LinearLayoutManager (this, LinearLayoutManager.VERTICAL, false));
rv.setAdapter (new RecyclerView.Adapter () {
class ViewHolder extends RecyclerView.ViewHolder {
private final TextView vItem;
public ViewHolder (TextView textView) {
super (textView);
vItem = textView;
}
public void bind (String itemName) {
vItem.setText (itemName);
vItem.setTag (itemName);
vItem.setOnDragListener (onDrag);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
TextView vText = new TextView (MainActivity.this);
vText.setTextSize (50);
return new ViewHolder (vText);
}
@Override
public void onBindViewHolder (RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder)
((ViewHolder) holder).bind (getItem (position));
}
@Override
public int getItemCount () {
return 100;
}
public String getItem (int position) {
return "Folder " + (1 + position);
}
});
}
protected void handleScroll (RecyclerView vList, View viewHoveredOver) {
LinearLayoutManager mgr = (LinearLayoutManager) vList.getLayoutManager ();
int iFirst = mgr.findFirstCompletelyVisibleItemPosition ();
int iLast = mgr.findLastCompletelyVisibleItemPosition ();
// Auto-Scroll:
if (mgr.findViewByPosition (iFirst) == viewHoveredOver)
vList.smoothScrollToPosition (Math.max (iFirst - 1, 0));
else if (mgr.findViewByPosition (iLast) == viewHoveredOver)
vList.smoothScrollToPosition (Math.min (iLast + 1,
mgr.getChildCount ()));
}
}
基本上每次“文件夹”被 ACTION_DRAG_ENTER 编辑时,都会调用 handleScroll () 方法:它会检查拖动的文件悬停在哪个文件夹上,并使用它来滚动 RecyclerView。
问题是RecyclerView的回收机制:据我了解,回收池中的 View 不是ACTION_DRAG_STARTED,所以自动滚动到 View 中的 View 不能接收文件,也不能自动滚动列表进一步。
如何在 RecyclerView 与其外部之间进行拖放操作?使用自动滚动?
有没有办法在拖动开始后将新 View 添加到拖动中?
谢谢。
最佳答案
我做了更多的研究,似乎ViewGroup和 View其中包含所需的信息:
1 的问题在于 mPrivateFlags2 是一个包访问专用变量:从包外部,它既不能通过直接代码访问,也不能通过反射访问(反射给出 IllegalAccessException)。另一个问题是 ViewGroup 只会将 DragEvent 分派(dispatch)给 ViewGroup 知道的 child ,不会碰巧设置了正确标志的 child (这可以在 dispatchDragEvent 中找到() 实现,例如在第 1421 行附近,如:for (View child : mDragNotifiedChildren)
...)。
2的问题在于notifyChildOfDrag()也是package-only成员。那么,我能想到的唯一答案是从 ACTION_DRAG_STARTED 保存 DragEvent,然后在 RecyclerView 添加新子项时重新发送事件。无论如何,这基本上就是 notifyChildOfDrag () 所做的;只有这也会通知已经通知的 child ,所以它不是最佳的。
为此,我采用了这种方式:
我还对代码进行了其他小的修复和调整,但这些是使其正常工作的主要步骤。
这是生成的新 Java 代码(XML 保持不变,所以我不会重新发布):
package net.wbord.recyclerviewdragdropautoscrolltest;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
// DEFINE HOLDER CLASS:
public static class DragEventHolder {
DragEvent mStartDrag = null;
}
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
// Find Views:
final TextView exampleItem = (TextView) findViewById (R.id.exampleItem);
final RecyclerView rv = (RecyclerView) findViewById (R.id.mainList);
// Define Drag Listener:
final DragEventHolder dragEventHolder = new DragEventHolder (); // PART OF ANSWER: VARIABLE TO HOLD DRAG EVENT
final View.OnDragListener onDrag = new View.OnDragListener () {
@Override
public boolean onDrag (View v, DragEvent event) {
switch (event.getAction ()) {
case DragEvent.ACTION_DRAG_STARTED:
dragEventHolder.mStartDrag = event; // PART OF ANSWER
v.setScaleX (1); v.setScaleY (1); // MINOR TWEAK (makes appearance better)
break;
case DragEvent.ACTION_DRAG_ENTERED:
v.setScaleX (1.5f); v.setScaleY (1.5f);
break;
case DragEvent.ACTION_DRAG_ENDED:
dragEventHolder.mStartDrag = null; // PART OF ANSWER
case DragEvent.ACTION_DRAG_EXITED:
v.setScaleX (1); v.setScaleY (1);
break;
case DragEvent.ACTION_DROP:
ClipData data = event.getClipData ();
String folder = (String) v.getTag ();
String msg = "File '" + data.getItemAt (0).getText () + "' " +
"moved into folder '" + folder + "'";
Toast.makeText (MainActivity.this, msg, Toast.LENGTH_LONG).show ();
break;
case DragEvent.ACTION_DRAG_LOCATION:
handleScroll (rv, v); // MINOR FIX: CALL handleScroll () FROM ACTION_DRAG_LOCATION RATHER THAN ACTION_DRAG_ENTERED (helps with easier auto-scrolling)
}
return true;
}
};
// The "file" for the user to drag-drop into a folder:
exampleItem.setOnLongClickListener (new View.OnLongClickListener () {
@Override
public boolean onLongClick (View v) {
// Start drag:
ClipData.Item item = new ClipData.Item (exampleItem.getText ());
ClipData data = new ClipData (exampleItem.getText (),
new String [] {ClipDescription.MIMETYPE_TEXT_PLAIN},
item);
View.DragShadowBuilder builder = new View.DragShadowBuilder (exampleItem);
v.startDrag (data, builder, null, 0);
return true;
}
});
// The list of "folders" that can accept the file:
final android.os.Handler updateDragHandler = new Handler ();
rv.setLayoutManager (new LinearLayoutManager (this, LinearLayoutManager.VERTICAL, false));
rv.setAdapter (new RecyclerView.Adapter () {
class ViewHolder extends RecyclerView.ViewHolder {
private final TextView vItem;
public ViewHolder (TextView textView) {
super (textView);
vItem = textView;
}
public void bind (String itemName) {
vItem.setText (itemName);
vItem.setTag (itemName);
vItem.setOnDragListener (onDrag);
// Re-send DragEvent:
updateDragHandler.postDelayed (new Runnable () {
@Override
public void run () {
ViewParent parent = vItem.getParent ();
if (parent == null || !(parent instanceof ViewGroup)) {
updateDragHandler.postDelayed (this, 50);
return;
}
if (dragEventHolder.mStartDrag != null)
((ViewGroup) parent).dispatchDragEvent (dragEventHolder.mStartDrag);
}
}, 100);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
TextView vText = new TextView (MainActivity.this);
vText.setTextSize (50);
return new ViewHolder (vText);
}
@Override
public void onBindViewHolder (RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder)
((ViewHolder) holder).bind (getItem (position));
}
@Override
public int getItemCount () {
return 100;
}
public String getItem (int position) {
return "Folder " + (1 + position);
}
});
}
protected void handleScroll (RecyclerView vList, View viewHoveredOver) {
LinearLayoutManager mgr = (LinearLayoutManager) vList.getLayoutManager ();
int iFirst = mgr.findFirstCompletelyVisibleItemPosition ();
int iLast = mgr.findLastCompletelyVisibleItemPosition ();
// Auto-Scroll:
if (mgr.findViewByPosition (iFirst) == viewHoveredOver)
vList.smoothScrollToPosition (Math.max (iFirst - 1, 0));
else if (mgr.findViewByPosition (iLast) == viewHoveredOver)
vList.smoothScrollToPosition (Math.min (iLast + 1,
vList.getAdapter ().getItemCount ())); // MINOR FIX: Was getting the wrong count before.
}
}
我试图通过评论来引起人们对这些变化的关注。
可以对这段代码进行一些优化,但主要思想就在那里。让我知道这种方法是否有明显错误。
关于Android:RecyclerView、拖放(外部 View )和自动滚动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34575100/
我在这里有一个我想要做的事情的例子:http://jsbin.com/OwoYAlEQ/1/edit 这是我的 HTML: person one person two person three per
我想知道是否有人知道是否有一个预先制定的解决方案:我在 ASP.net 网站上有一个列表,我希望用户能够通过拖放对列表进行重新排序。此外,我希望有第二个列表,用户可以将第一个列表中的项目拖到其中。 到
我想知道是否有人知道是否有一个预先制定的解决方案:我在 ASP.net 网站上有一个列表,我希望用户能够通过拖放对列表进行重新排序。此外,我希望有第二个列表,用户可以将第一个列表中的项目拖到其中。 到
我在我的 Web 应用程序中使用 Ajax ControlToolkit 中的 ModalPopupExtender。我将其 Drag 属性设置为 true,但是当我拖动弹出面板并将其放到新位置时,它
所以,基于this answer ,我有一组可以拖放并卡入到位的 div。唯一的问题是,可拖动的 div 具有不同的高度,我需要它们始终捕捉到目标的底部,而不是顶部。 您可以在this JsFiddl
我一直在使用 Bea 的解决方案 here一段时间后发现它非常有帮助。现在我遇到的问题是,当我将项目拖放到另一个 ListView 控件中或拖放到另一个 ListView 控件中,并且我想在拖动“期间
我试图在使用 QTreeWidget.setItemWidget() 重新父级(拖放)后将小部件放入 QTreeWidgetItem 但是,如果编译以下代码,结果是 QTreeWidgetItem 内
这是场景,我使用的是prototype和scriptaculous,但我相信jquery也会有同样的问题。我在相对定位的 div 中有一个可拖动图像的列表。问题是我无法将图像拖出父 div...好吧.
我正在使用一个普通(Bootstrap)表,我想在其中包含可排序的行。我正在使用 Angular CDK (DragDropModule) 来实现排序/排序。但是,当拖动行时,它会扭曲宽度,因为 cd
我正在尝试在我的 UICollectionView 中实现拖放机制,这与在快捷方式应用程序中重新排序快捷方式的组件非常相似。 截至目前,行为是当您开始拖动时,会留下一个透明的单元格 View ,而另一
我有以下 Jquery UI 拖放 jsfiddle https://jsfiddle.net/zoojsfiddle/ud96jdcp/ 具有
我希望创建一个基于网络的“公告板”,可以这么说,用户可以在其中创建/删除/拖放“图钉”,而不允许重叠“图钉”。 这是一个图表,应该有助于说明我正在尝试创建的内容: 'pins' 可能已创建双击;他们会
我是一名优秀的程序员,十分优秀!