Вопрос

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).

Cause of crash

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.

From 4.4.2 source:

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.

First workaround:

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).

Second workaround: *EDITED (see below)

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.

Другие советы

NVM, found the issue. Turns out its probably a bug in the framework.

https://code.google.com/p/android/issues/detail?id=21775

reports the same problem with nexus.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top