I am sorry if I have done any silly mistake, but I cant find the source of this: I am trying to implement a onDrag listener on my EditText "etItem" This is my code:
etItem.setOnDragListener(new OnDragListener(){
@Override
public boolean onDrag(View v, DragEvent dragevent) {
if(null!=dragevent && null!=v){
if( dragevent.getAction() == DragEvent.ACTION_DROP )
{
View view = (View) dragevent.getLocalState();
ViewGroup owner = (ViewGroup) view.getParent();
int itemNum = (Integer) view.getTag();
itemAmounts[itemNum] = 0;
owner.removeView(view);
return true;
}
}
return false;
}});
This is working fine on my Samsng Galaxy Grand, But giving me a nullpointer on Nexus.
The stackTrace:
03-26 15:47:21.185: E/AndroidRuntime(1439): FATAL EXCEPTION: main
03-26 15:47:21.185: E/AndroidRuntime(1439): java.lang.NullPointerException
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.widget.Editor.onDrop(Editor.java:1797)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.widget.TextView.onDragEvent(TextView.java:8350)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.View.dispatchDragEvent(View.java:16375)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1237)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewRootImpl.handleDragEvent(ViewRootImpl.java:3838)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewRootImpl.access$600(ViewRootImpl.java:95)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2999)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.os.Handler.dispatchMessage(Handler.java:99)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.os.Looper.loop(Looper.java:137)
03-26 15:47:21.185: E/AndroidRuntime(1439): at android.app.ActivityThread.main(ActivityThread.java:5041)
03-26 15:47:21.185: E/AndroidRuntime(1439): at java.lang.reflect.Method.invokeNative(Native Method)
03-26 15:47:21.185: E/AndroidRuntime(1439): at java.lang.reflect.Method.invoke(Method.java:511)
03-26 15:47:21.185: E/AndroidRuntime(1439): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-26 15:47:21.185: E/AndroidRuntime(1439): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-26 15:47:21.185: E/AndroidRuntime(1439): at dalvik.system.NativeStart.main(Native Method)
03-26 15:47:21.345: W/ActivityManager(297): Force finishing activity com.listcalc.main/.MainActivityFree
I think this is a bug with EditText
widgets and the Drag and Drop API in Android. I added a post to the issue tracker linked in the other solution, but I'll reproduce it here for easy reading. If you experience this problem, please go to the tracker though and star the issue to hopefully make the Android engineers aware of it (wishful thinking, I know).
I came across this crash/bug in 4.4.2 while trying to use the Drag and Drop API to move an EditText
around in a FrameLayout
(very simple goal). I only have an OnDragListener
set on my FrameLayout
, but apparently the framework calls all the children View
's OnDragEvent()
callbacks as can be seen in the crash stacktrace.
The crash occurs in the internal Class android.widget.Editor.onDrop()
where the method tries to access ClipData
info from the DragEvent
that is passed to the method.
1828 void onDrop(DragEvent event) {
1829 StringBuilder content = new StringBuilder("");
1830 ClipData clipData = event.getClipData();
1831 final int itemCount = clipData.getItemCount();
1832 for (int i=0; i < itemCount; i++) {
1833 Item item = clipData.getItemAt(i);
1834 content.append(item.coerceToStyledText(mTextView.getContext()));
1835 }
....
In line 1830 event.getClipData()
returns null
. Then in line 1831 clipData.getItemCount()
will trigger an NPE. This only seems to happen with EditText
's since they have a android.widget.Editor
instance field, which leads me to think this is a bug/oversight.
I found two workarounds for this.
Similar to the workaround proposed in comment #1, you can set a dummy OnDragListener
on your EditText
objects that are in the hierarchy of Views that will receive Drag events. However, the dummy listener can simply be
myEditText.setOnDragListener( new View.OnDragListener() {
@Override
public boolean onDrag( View v, DragEvent event) {
return true;
}
});
By just returning true
for all Drag events you'll be telling the framework you've handled the event no matter what the type of Action was (i.e. the action you get from calling event.getAction()
). This means you'll intercept the Drag event and it won't be passed any further to the View's actual OnDragEvent()
callback, therefore avoiding the crash that we see in the stacktrace.
It's not necessary to have a separate condition for (event.getAction() == DragEvent.ACTION_DROP)
as seen in comment #1 for this dummy listener, since if you return false for ACTION_DRAG_STARTED
you'll never receive the ACTION_DROP
event anyway (you can confirm this by placing a Log print message in the ACTION_DROP
condition and you'll notice it never gets called because you already returned false
in the else
condition).
Since the NPE occurs when trying to access null
ClipData
in Editor.onDrop()
, you can simply provide a dummy ClipData
to the DragEvent
when you begin the dragging process.
This is how I did it for my scenario:
... <other code>
ClipData dummyData = ClipData.newPlainText("dummyData", ""); // don't forget to pass empty String
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
v.startDrag(dummyData, shadowBuilder, v, 0);
...
I chose to use the helper method ClipData.newPlainText
to just create a simple plain text ClipData
for me since you can't just construct a ClipData
with its constructors and null
parameters because that will lead to crashes also.
So where ever it was you begin your drag process with View.startDrag()
be sure to provide this dummy ClipData
. With this workaround I don't need the dummy OnDragListener
and I don't get any NPE crashes.
NOTE: if you use the first workaround, know that by returning true
from the dummy OnDragListener
you'll effectively stop the DragEvent from propagating to TextView.onDragEvent()
so none of the code that lives in there will be called. I'm unsure of the side-effects of this if any, but it's definitely not an empty method.
*EDIT: Upon playing around with Drag and Drop with EditText
widgets some more, I discovered that there's some really weird behavior with TextView.onDragEvent()
which is what eventually gets called if you go with the second workaround. I noticed that when using the dummy ClipData
that I suggested, the "dummyData" string that you pass as the second argument to ClipData.newPlainText()
will sometimes get pasted/inserted into the EditText
if you try to drag the widget around a bit while also moving the caret within the text box, or by typing into the EditText
and then dragging it after.
It's very confusing to explain, but I was able to reproduce it reliably. Any readers can take a look at the source code for the method if they want to try and make sense of it, but I'm honestly tired of trying to figure out the weird intentions the Android Engineers had for this. Because of this odd behavior I altered my second workaround to pass an empty String as the second argument instead. This seems to alleviate the problem.
Personally I ended up extending the EditText
class and making my own and simply overriding onDragEvent()
to return true;
and not call through to the superclass (i.e. TextView.onDragEvent()
. This is in essence the same as First workaround but I needed to extend the EditText
class anyway to add some extra functionality. Whatever the original code in TextView.onDragEvent()
does, skipping it doesn't seem to affect my personal use case and I don't notice any lack of functionality that I needed.