Android Open Source - pixel-art Layer Fragment






From Project

Back to project page pixel-art.

License

The source code is released under:

Apache License

If you think the Android project pixel-art listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.jaween.pixelart.ui.layer;
//from w w w  . ja  va 2 s .  co  m
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;

import com.jaween.pixelart.R;
import com.jaween.pixelart.ui.animation.Frame;
import com.jaween.pixelart.ui.undo.LayerUndoData;
import com.jaween.pixelart.ui.undo.UndoItem;
import com.jaween.pixelart.ui.undo.UndoManager;
import com.jaween.pixelart.util.ConfigChangeFragment;
import com.mobeta.android.dslv.DragSortListView;

import java.util.LinkedList;

/**
 * Created by ween on 11/27/14.
 */
public class LayerFragment extends Fragment implements
        View.OnClickListener,
        ListView.OnItemClickListener,
        LayerAdapter.LayerListItemListener,
        DragSortListView.DropListener,
        DragSortListView.RemoveListener {

    // ListView data
    private LinkedList<Frame> frames = null;
    private LayerAdapter layerAdapter;

    // Views
    private DragSortListView layerDragSortListView;
    private ImageButton layerAdd;
    private ImageButton layerDuplicate;

    // Layer properties
    private static final int MAX_LAYER_COUNT = 20;
    private int currentFrameIndex = 0;
    private int currentLayerIndex = 0;
    private int layerWidth;
    private int layerHeight;
    private Bitmap.Config config = Bitmap.Config.ARGB_8888;

    // Layer callbacks
    private LayerListener layerListener;

    // Undo management
    private UndoManager undoManager;

    // Fragment save state
    private static final String KEY_SCROLL_INDEX = "key_scroll_index";
    private static final String KEY_SCROLL_OFFSET = "key_scroll_offset";
    private static final String KEY_LAYER_INDEX = "key_layer_index";

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_SCROLL_INDEX, layerDragSortListView.getFirstVisiblePosition());

        // Offset of top-most visible child
        View item = layerDragSortListView.getChildAt(0);
        int offset;
        if (item != null) {
            offset = item.getTop();
        } else {
            offset = 0;
        }
        outState.putInt(KEY_SCROLL_OFFSET, offset);
    }

    private void onRestoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            // TODO: Perhaps remove scroll pos restore if it's already in the DSLV
            // Restores scroll position
            int scrollIndex = savedInstanceState.getInt(KEY_SCROLL_INDEX, 0);
            int scrollOffset = savedInstanceState.getInt(KEY_SCROLL_OFFSET, 0);
            // TODO: Fix, surely this wasn't added in API 21, it was fine in other lists
            layerDragSortListView.setSelectionFromTop(scrollIndex, scrollOffset);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.layer_fragment, null);

        layerDragSortListView = (DragSortListView) view.findViewById(R.id.dslv_layer_list);
        layerAdd = (ImageButton) view.findViewById(R.id.ib_layer_add);
        layerDuplicate = (ImageButton) view.findViewById(R.id.ib_layer_duplicate);

        onRestoreInstanceState(savedInstanceState);


        layerDragSortListView.setOnItemClickListener(this);
        layerDragSortListView.setRemoveListener(this);
        layerDragSortListView.setDropListener(this);
        layerAdd.setOnClickListener(this);
        layerDuplicate.setOnClickListener(this);

        return view;
    }

    // Sets the undo manager for undoing and redoing layer commands
    public void setUndoManager(UndoManager undoManager) {
        this.undoManager = undoManager;
    }

    /** Creates and returns an animation Frame without storing it here, used for future frame
     * changes. The composite Bitmap is blank. **/
    public Frame requestFrame(boolean duplicate) {
        LinkedList<Layer> layers = new LinkedList<Layer>();

        Layer layer = createLayer(duplicate);
        layers.add(layer);
        Bitmap compositeBitmap = Bitmap.createBitmap(layerWidth, layerHeight, config);
        int index = 0;
        Frame frame = new Frame(layers, compositeBitmap, index);

        return frame;
    }

    /** Changes the working set of layers, updates the list adapter. **/
    public void setFrames(LinkedList<Frame> frames) {
        this.frames = frames;

        setListAdapter();
    }

    /** Initialises the drawing dimensions. Must be called before requesting a Frame **/
    public void setDimensions(int layerWidth, int layerHeight) {
        this.layerWidth = layerWidth;
        this.layerHeight = layerHeight;
    }

    public void setCurrentFrameIndex(int index) {
        currentFrameIndex = index;
        setListAdapter();
    }

    /*private void setCurrentLayerIndex(int index) {
        currentLayerIndex = index;
    }*/

    public void setListAdapter() {
        // TODO: Is there a better way? (test case, new image > add layer > add frame > switch to that frame : should have only a single layer)
        // New list adapter every time the user changes frames
        layerAdapter = new LayerAdapter(getActivity(), frames.get(currentFrameIndex).getLayers());
        layerAdapter.setLayerListItemListener(this);
        layerDragSortListView.setAdapter(layerAdapter);
    }

    /** Creates and returns a new Layer instance **/
    private Layer createLayer(boolean duplicate) {
        // Creates the bitmap that belongs to the new layer
        Bitmap image;
        if (frames != null) {
            LinkedList<Layer> layers = frames.get(currentFrameIndex).getLayers();
            if (duplicate) {
                image = layers.get(currentLayerIndex).getImage().copy(config, true);
            } else {
                image = Bitmap.createBitmap(layerWidth, layerHeight, config);
            }
        } else {
            image = Bitmap.createBitmap(layerWidth, layerHeight, config);
        }

        // Creates the new layer object
        int size = frames == null ? 0 : frames.get(currentFrameIndex).getLayers().size();
        String title = getString(R.string.layer) + " " + (size + 1);
        return new Layer(image, title);
    }

    /** Creates and adds a new Layer to the data structure, updates the UI accordingly. **/
    private Layer addLayer(int frameIndex, int layerIndex, boolean duplicate) {
        // Creates a new layer and updates the data structure
        LinkedList<Layer> layers = frames.get(frameIndex).getLayers();
        Layer layer = createLayer(duplicate);
        layers.add(layerIndex, layer);

        // If the current layer has moved, points the currentLayerIndex at it's new position
        currentLayerIndex = layerIndex;
        if (layers.size() > 1) {
            notifyCurrentLayerChanged();
        }

        // Updates the UI
        layerAdapter.notifyDataSetChanged();

        // Scrolls to the new item in the list. This is similar to using the 'Transcript mode', but
        // it also can maintain the scroll position on config change
        layerDragSortListView.smoothScrollToPosition(layerIndex);

        Log.d("LayerFragment", "Layer added, size is now " + layers.size() + ", frame index is " + frameIndex);

        return layer;
    }

    /** Deletes and returns the Layer at the given index and updates the UI accordingly **/
    private Layer deleteLayer(int frameIndex, int layerIndex) {
        LinkedList<Layer> layers = frames.get(frameIndex).getLayers();
        Layer currentLayer = layers.get(currentLayerIndex);
        Layer deleteLayer = layers.get(layerIndex);

        // Updates the data structure and animates out the item
        layers.remove(layerIndex);

        // If the current layer has moved, points the currentLayerIndex at it's new position
        if (layerIndex == currentLayerIndex && layerIndex == layers.size()) {
            // Layer deleted was the current layer and it was the bottom most layer
            // New current layer is the one above it
            if (layers.size() > 0) {
                currentLayerIndex--;
                notifyCurrentLayerChanged();
            }
        } else {
            // Finds the position of the new layer
            int newCurrentLayerIndex = layers.indexOf(currentLayer);
            if (newCurrentLayerIndex != -1) {
                if (currentLayerIndex != newCurrentLayerIndex) {
                    currentLayerIndex = newCurrentLayerIndex;
                    notifyCurrentLayerChanged();
                }
            }
        }

        // Updates the UI
        layerAdapter.notifyDataSetChanged();

        return deleteLayer;
    }

    /**
     * Moves a layer in the data set. This does not push/pop the undo stack.
     * @param frameIndex The index of the Frame in which this operation is taking place
     * @param from The source index from which the layer must move
     * @param to The destination index to which the layer must move
     */
    private void moveLayer(int frameIndex, int from, int to) {
        LinkedList<Layer> layers = frames.get(frameIndex).getLayers();
        Layer currentLayer = layers.get(currentLayerIndex);

        Layer movingLayer = layers.remove(from);
        layers.add(to, movingLayer);
        layerAdapter.notifyDataSetChanged();

        int newCurrentLayerIndex = layers.indexOf(currentLayer);
        if (currentLayerIndex != newCurrentLayerIndex) {
            currentLayerIndex = newCurrentLayerIndex;
            notifyCurrentLayerChanged();
        }
    }

    private void pushAddItem(int frameIndex, int layerIndex, Layer layer) {
        LayerUndoData layerUndoData = new LayerUndoData(
                LayerUndoData.LayerOperation.ADD,
                frameIndex,
                layerIndex,
                layer);
        UndoItem undoItem = new UndoItem(UndoItem.Type.LAYER, 0, layerUndoData);
        undoManager.pushUndoItem(undoItem);
    }

    private void pushDeleteItem(int frameIndex, int layerIndex, Layer layer) {
        LayerUndoData layerUndoData = new LayerUndoData(
                LayerUndoData.LayerOperation.DELETE,
                frameIndex,
                layerIndex,
                layer);
        UndoItem undoItem = new UndoItem(UndoItem.Type.LAYER, 0, layerUndoData);
        undoManager.pushUndoItem(undoItem);
    }

    /**
     * Pushes an undo item stating that a layer has moved places.
     * @param from The source index from which the layer has already moved
     * @param to The destination index to which the layer has already moved
     */
    private void pushMoveItem(int from, int to, int frameindex) {
        LayerUndoData layerUndoData = new LayerUndoData(from, to, frameindex);
        UndoItem undoItem = new UndoItem(UndoItem.Type.LAYER, 3, layerUndoData);
        undoManager.pushUndoItem(undoItem);
    }

    public void undo(Object undoData) {
        if (undoData instanceof LayerUndoData) {
            switch (((LayerUndoData) undoData).getType()) {
                case ADD:
                    int frameIndex = ((LayerUndoData) undoData).getFrameIndex();
                    int layerIndex = ((LayerUndoData) undoData).getLayerIndex();
                    deleteLayer(frameIndex, layerIndex);
                    break;
                case DELETE:
                    frameIndex = ((LayerUndoData) undoData).getFrameIndex();
                    layerIndex = ((LayerUndoData) undoData).getLayerIndex();
                    Layer layer = ((LayerUndoData) undoData).getLayer();
                    LinkedList<Layer> layers = frames.get(frameIndex).getLayers();
                    layers.add(layerIndex, layer);
                    layerAdapter.notifyDataSetChanged();
                    notifyCurrentLayerChanged();
                    break;
                case MOVE:
                    frameIndex = ((LayerUndoData) undoData).getFrameIndex();
                    int fromIndex = ((LayerUndoData) undoData).getFromIndex();
                    int toIndex = ((LayerUndoData) undoData).getToIndex();
                    moveLayer(frameIndex, toIndex, fromIndex);
                    break;
                case MERGE:
                    //TODO: Undo merge layers
                    break;
            }
        } else {
            String className = "Null";

            if (undoData != null) {
                className = undoData.getClass().getName();
            }
            Log.e("DrawingSurface", "Undo data wasn't of type DrawOpUndoData, it was of type " + className);
        }
    }

    public void redo(Object redoData) {
        if (redoData instanceof LayerUndoData) {
            switch (((LayerUndoData) redoData).getType()) {
                case ADD:
                    int frameIndex = ((LayerUndoData) redoData).getFrameIndex();
                    int layerIndex = ((LayerUndoData) redoData).getLayerIndex();
                    Layer layer = ((LayerUndoData) redoData).getLayer();
                    LinkedList<Layer> layers = frames.get(frameIndex).getLayers();
                    layers.add(layerIndex, layer);
                    layerAdapter.notifyDataSetChanged();
                    break;
                case DELETE:
                    frameIndex = ((LayerUndoData) redoData).getFrameIndex();
                    layerIndex = ((LayerUndoData) redoData).getLayerIndex();
                    deleteLayer(frameIndex, layerIndex);
                    break;
                case MOVE:
                    frameIndex = ((LayerUndoData) redoData).getFrameIndex();
                    int fromIndex = ((LayerUndoData) redoData).getFromIndex();
                    int toIndex = ((LayerUndoData) redoData).getToIndex();
                    moveLayer(frameIndex, fromIndex, toIndex);
                    break;
                case MERGE:
                    //TODO: Redo merge layers
                    break;
            }
        } else {
            String className = "Null";

            if (redoData != null) {
                className = redoData.getClass().getName();
            }
            Log.e("DrawingSurface", "Redo data wasn't of type DrawOpUndoData, it was of type " + className);
        }
    }

    public void invalidate() {
        layerAdapter.notifyDataSetChanged();
    }

    /** Notifies the SurfaceView to perform future draw operations on the new current layer **/
    public void notifyCurrentLayerChanged() {
        if (layerListener != null) {
            layerListener.onCurrentLayerChange(currentLayerIndex);
        }
        activate(currentLayerIndex);
    }

    @Override
    public void onClick(View view) {
        LinkedList<Layer> layers = frames.get(currentFrameIndex).getLayers();
        switch (view.getId()) {
            case R.id.ib_layer_add:
                if (layers.size() < MAX_LAYER_COUNT) {
                    int frameIndex = currentFrameIndex;
                    int layerIndex = currentLayerIndex;
                    boolean duplicate = false;
                    Layer layer = addLayer(frameIndex, layerIndex, duplicate);
                    pushAddItem(frameIndex, layerIndex, layer);
                }
                break;
            case R.id.ib_layer_duplicate:
                if (layers.size() < MAX_LAYER_COUNT) {
                    int frameIndex = currentFrameIndex;
                    int layerIndex = currentLayerIndex;
                    boolean duplicate = true;
                    Layer layer = addLayer(frameIndex, layerIndex, duplicate);
                    pushAddItem(frameIndex, layerIndex, layer);
                }
                break;
        }
    }

    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
        currentLayerIndex = i;
        activate(currentLayerIndex);
        layerAdapter.notifyDataSetChanged();
        notifyCurrentLayerChanged();
    }

    public void setLayerListener(LayerListener layerListener) {
        this.layerListener = layerListener;
    }

    @Override
    public void onDeleteLayerFromList(int index) {
        layerDragSortListView.removeItem(index);
    }

    @Override
    public void drop(int from, int to) {
        moveLayer(currentFrameIndex, from, to);
        pushMoveItem(from, to, currentFrameIndex);
    }

    @Override
    public void remove(int layerIndex) {
        Layer layer = deleteLayer(currentFrameIndex, layerIndex);
        pushDeleteItem(currentFrameIndex, layerIndex, layer);
        notifyCurrentLayerChanged();
    }

    private void activate(int layerIndex) {
        layerDragSortListView.setSelection(layerIndex);
        layerAdapter.setCurrentLayerIndex(layerIndex);
    }

    public interface LayerListener {
        public void onCurrentLayerChange(int index);
        public void onMergeLayer(int index);
    }
}




Java Source Code List

com.jaween.pixelart.ContainerActivity.java
com.jaween.pixelart.ContainerFragment.java
com.jaween.pixelart.PanelManagerFragment.java
com.jaween.pixelart.io.AnimationFile.java
com.jaween.pixelart.io.FileAdapter.java
com.jaween.pixelart.io.ImportExport.java
com.jaween.pixelart.io.LoadFileDialog.java
com.jaween.pixelart.tools.Command.java
com.jaween.pixelart.tools.Dropper.java
com.jaween.pixelart.tools.Eraser.java
com.jaween.pixelart.tools.FloodFill.java
com.jaween.pixelart.tools.FreeSelect.java
com.jaween.pixelart.tools.MagicWand.java
com.jaween.pixelart.tools.Oval.java
com.jaween.pixelart.tools.Pen.java
com.jaween.pixelart.tools.RectSelect.java
com.jaween.pixelart.tools.Rect.java
com.jaween.pixelart.tools.Selection.java
com.jaween.pixelart.tools.ToolReport.java
com.jaween.pixelart.tools.Tool.java
com.jaween.pixelart.tools.attributes.EraserToolAttributes.java
com.jaween.pixelart.tools.attributes.MagicWandToolAttributes.java
com.jaween.pixelart.tools.attributes.OvalToolAttributes.java
com.jaween.pixelart.tools.attributes.PenToolAttributes.java
com.jaween.pixelart.tools.attributes.RectToolAttributes.java
com.jaween.pixelart.tools.attributes.ToolAttributes.java
com.jaween.pixelart.tools.options.EraserOptionsView.java
com.jaween.pixelart.tools.options.MagicWandOptionsView.java
com.jaween.pixelart.tools.options.OvalOptionsView.java
com.jaween.pixelart.tools.options.PenOptionsView.java
com.jaween.pixelart.tools.options.RectOptionsView.java
com.jaween.pixelart.tools.options.ToolOptionsView.java
com.jaween.pixelart.ui.ColourButton.java
com.jaween.pixelart.ui.ColourSelector.java
com.jaween.pixelart.ui.DrawingFragment.java
com.jaween.pixelart.ui.DrawingSurface.java
com.jaween.pixelart.ui.PaletteFragment.java
com.jaween.pixelart.ui.PanelFragment.java
com.jaween.pixelart.ui.PixelGrid.java
com.jaween.pixelart.ui.Thumbnail.java
com.jaween.pixelart.ui.ToolButton.java
com.jaween.pixelart.ui.ToolboxFragment.java
com.jaween.pixelart.ui.TransparencyCheckerboard.java
com.jaween.pixelart.ui.animation.AnimationFragment.java
com.jaween.pixelart.ui.animation.FrameAdapter.java
com.jaween.pixelart.ui.animation.Frame.java
com.jaween.pixelart.ui.colourpicker.ColourPickerDialog.java
com.jaween.pixelart.ui.colourpicker.ColourPickerFragment.java
com.jaween.pixelart.ui.colourpicker.ColourPickerView.java
com.jaween.pixelart.ui.layer.LayerAdapter.java
com.jaween.pixelart.ui.layer.LayerFragment.java
com.jaween.pixelart.ui.layer.Layer.java
com.jaween.pixelart.ui.undo.DrawOpManager.java
com.jaween.pixelart.ui.undo.DrawOpUndoData.java
com.jaween.pixelart.ui.undo.FrameUndoData.java
com.jaween.pixelart.ui.undo.LayerUndoData.java
com.jaween.pixelart.ui.undo.UndoItem.java
com.jaween.pixelart.ui.undo.UndoManager.java
com.jaween.pixelart.util.AbsVerticalSeekBar.java
com.jaween.pixelart.util.AnimatedGifEncoder.java
com.jaween.pixelart.util.AutoSaver.java
com.jaween.pixelart.util.BitmapEncoder.java
com.jaween.pixelart.util.Color.java
com.jaween.pixelart.util.ConfigChangeFragment.java
com.jaween.pixelart.util.Debug.java
com.jaween.pixelart.util.MarchingAnts.java
com.jaween.pixelart.util.PreferenceManager.java
com.jaween.pixelart.util.ScaleListener.java
com.jaween.pixelart.util.SlideAnimator.java
com.jaween.pixelart.util.SlidingLinearLayout.java
com.jaween.pixelart.util.VerticalProgressBar.java
com.tokaracamara.android.verticalslidevar.VerticalSeekBar.java