dimanche 10 mai 2015

Android: Rearrange views using drag-and-drop

I'm trying to rearrange views (cards, in particular) using drag-and-drop.

My initial thought for a simple implementation is to reposition a view by removing it from the parent and then adding it to the spot on the left or on the right.

This seems to work fine without animations, but once I start using animations, sometimes cards get stuck. I get the error "java.lang.NullPointerException: Attempt to read from field 'android.view.IWindowSession android.view.View$AttachInfo.mSession' on a null object reference at android.view.View.startDrag(View.java:18353) at cz.cvut.mazelmir.cards.Cards$AFragment$2.onTouch(Anagram.java:158)" -- I'm not quite sure what to make of that, as the things passed to onTouch are the view being touched (shouldn't be null) and newly created parameters.

Any clue as to why this happening and what I could do to fix it? Or at least some hints as to how to best implement this drag-and-drop functionality? (Perhaps a simple frame view where I just animate the cards?)

Here's the code for the fragment containing the cards: (The fragment layout is just a linear layout, and the card layout contains a textview.)

public static class AFragment extends Fragment {

    String s = "string";
    ViewGroup emptyContainer;
    View[] cards;
    float[] cardLoc;

    public AFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, final ViewGroup container,
                             Bundle savedInstanceState) {
        LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.fragment_anagram, container, false);
        emptyContainer = null;
        cards = null;
        cardLoc = null;

        createCards(inflater, linearLayout, s);

        linearLayout.setOnDragListener(new View.OnDragListener() {
            @Override
            public boolean onDrag(View view, DragEvent event) {
                View draggedItem = (View) event.getLocalState();
                ViewGroup viewGroup = (ViewGroup) view;
                switch (event.getAction()) {
                    case DragEvent.ACTION_DRAG_STARTED:
                        if (cardLoc == null)
                            createPositions();
                        emptyContainer = viewGroup;
                        draggedItem.setVisibility(View.INVISIBLE);
                        break;
                    case DragEvent.ACTION_DRAG_LOCATION:
                        int emptyPos = (int)draggedItem.getTag(R.id.position_tag);
                        float currentX = event.getX();

                        // To the right?
                        if (emptyPos < cardLoc.length - 1 && currentX > cardLoc[emptyPos + 1]) {
                            viewGroup.removeView(cards[emptyPos + 1]);
                            viewGroup.addView(cards[emptyPos + 1], emptyPos);

                            TranslateAnimation translateAnimation = new TranslateAnimation(cardLoc[emptyPos + 1] - cardLoc[emptyPos], 0, 0, 0);
                            translateAnimation.setDuration(200);
                            cards[emptyPos + 1].startAnimation(translateAnimation);

                            View tempView = cards[emptyPos + 1];
                            cards[emptyPos + 1] = cards[emptyPos];
                            cards[emptyPos] = tempView;

                            draggedItem.setTag(R.id.position_tag, emptyPos + 1);
                            tempView.setTag(R.id.position_tag, emptyPos);
                        }
                        // To the left?
                        else if (emptyPos > 0 && currentX < cardLoc[emptyPos]) {
                            viewGroup.removeView(cards[emptyPos - 1]);
                            viewGroup.addView(cards[emptyPos - 1], emptyPos);

                            TranslateAnimation translateAnimation = new TranslateAnimation(cardLoc[emptyPos - 1] - cardLoc[emptyPos], 0, 0, 0);
                            translateAnimation.setDuration(200);
                            cards[emptyPos - 1].startAnimation(translateAnimation);

                            View tempView = cards[emptyPos - 1];
                            cards[emptyPos - 1] = cards[emptyPos];
                            cards[emptyPos] = tempView;

                            draggedItem.setTag(R.id.position_tag, emptyPos - 1);
                            tempView.setTag(R.id.position_tag, emptyPos);
                        }

                        break;
                    case DragEvent.ACTION_DRAG_ENDED:
                        draggedItem.setVisibility(View.VISIBLE);
                        break;
                    default:
                        break;
                }
                return true;
            }
        });

        return linearLayout;
    }

    private void createCards(LayoutInflater inflater, LinearLayout container, String s) {
        cards = new View[s.length()];
        int j = 0;
        for (char c : s.toCharArray()) {
            String letter = String.valueOf(c);

            View letterCard = inflater.inflate(R.layout.letter_container, container, false);
            TextView letterTextView = (TextView) letterCard.findViewById(R.id.letter_text);
            letterTextView.setText(letter);
            letterCard.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        ClipData data = ClipData.newPlainText("", "");
                        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
                        v.startDrag(data, shadowBuilder, v, 0);
                        return true;
                    }
                    return false;
                }
            });
            cards[j] = letterCard;
            container.addView(letterCard);

            letterCard.setTag(R.id.position_tag, j);
            letterCard.setTag(R.id.letter_tag, letter);

            j++;
        }
    }

    public void createPositions() {
        cardLoc = new float[cards.length];
        for (int i = 0; i < cards.length; i++) {
            cardLoc[i] = cards[i].getX();
        }
    }
}

}

Aucun commentaire:

Enregistrer un commentaire