2D Array grid on drawing canvas

I am writing an pixel art app that paints images the user draws on the screen to pixel look. There are two ways to approach it. Either pixelate the image after saving or have a grid before hand so the user draws the pixel image. I don't find anything on the later method. So my issue with the first is drawing a grid where if a cell is touched I would change the color of it. I tried drawing rectangles on a canvas but that was pointless because i couldn't control the cells.

I was thinking about nested for loops that creates a bitmap at each cell?

Answers


The following is just a simple, illustrative example. It is not optimized, implements no exception handling, etc.

public class PixelGridView extends View {
    private int numColumns, numRows;
    private int cellWidth, cellHeight;
    private Paint blackPaint = new Paint();
    private boolean[][] cellChecked;

    public PixelGridView(Context context) {
        this(context, null);
    }

    public PixelGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        blackPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    public void setNumColumns(int numColumns) {
        this.numColumns = numColumns;
        calculateDimensions();
    }

    public int getNumColumns() {
        return numColumns;
    }

    public void setNumRows(int numRows) {
        this.numRows = numRows;
        calculateDimensions();
    }

    public int getNumRows() {
        return numRows;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        calculateDimensions();
    }

    private void calculateDimensions() {
        if (numColumns < 1 || numRows < 1) {
            return;
        }

        cellWidth = getWidth() / numColumns;
        cellHeight = getHeight() / numRows;

        cellChecked = new boolean[numColumns][numRows];

        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);

        if (numColumns == 0 || numRows == 0) {
            return;
        }

        int width = getWidth();
        int height = getHeight();

        for (int i = 0; i < numColumns; i++) {
            for (int j = 0; j < numRows; j++) {
                if (cellChecked[i][j]) {

                    canvas.drawRect(i * cellWidth, j * cellHeight,
                                    (i + 1) * cellWidth, (j + 1) * cellHeight,
                                    blackPaint);
                }
            }
        }

        for (int i = 1; i < numColumns; i++) {
            canvas.drawLine(i * cellWidth, 0, i * cellWidth, height, blackPaint);
        }

        for (int i = 1; i < numRows; i++) {
            canvas.drawLine(0, i * cellHeight, width, i * cellHeight, blackPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            int column = (int)(event.getX() / cellWidth);
            int row = (int)(event.getY() / cellHeight);

            cellChecked[column][row] = !cellChecked[column][row];
            invalidate();
        }

        return true;
    }
}

Here's a simple Activity for demonstration:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        PixelGridView pixelGrid = new PixelGridView(this);
        pixelGrid.setNumColumns(4);
        pixelGrid.setNumRows(6);

        setContentView(pixelGrid);
    }
}

example

public class PixelGridView extends View {


//number of row and column 

    int horizontalGridCount = 2;


    private Drawable horiz;
    private Drawable vert;
    private final float width;



    public PixelGridView(@NonNull Context context) {
        this(context, null);
    }

    public PixelGridView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        horiz = new ColorDrawable(Color.WHITE); horiz.setAlpha(160);
        vert = new ColorDrawable(Color.WHITE); vert.setAlpha(160);
        width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.9f, context.getResources().getDisplayMetrics());
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        horiz.setBounds(left, 0, right, (int) width);
        vert.setBounds(0, top, (int) width, bottom);
    }





    private float getLinePosition(int lineNumber) {
        int lineCount = horizontalGridCount;

            return (1f / (lineCount + 1)) * (lineNumber + 1f);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
       // drawTask.start();
        int count = horizontalGridCount;
        for (int n = 0; n < count; n++) {
            float pos = getLinePosition(n);

            // Draw horizontal line
            canvas.translate(0, pos * getHeight());
            horiz.draw(canvas);
            canvas.translate(0, - pos * getHeight());

            // Draw vertical line
            canvas.translate(pos * getWidth(), 0);
            vert.draw(canvas);
            canvas.translate(- pos * getWidth(), 0);
        }
        //drawTask.end(count);
    }

and in your main activity:

//inside on create method

        val myView = PixelGridView(this)

        id_frame.addView(myView)

id_frame is frame layout in xml


One available option is to look into using the Android Gridview as the drawing grid; I have not tested this myself, however if you create an object to be touched in each cell with your desired pixel dimensions, you should be able to create a rudimentary Pixel Art application by saving the variables.

Note, grid view cells are sized based on their contents, as noted in How to set a cell size in Android grid view?

Also, when it comes to drawing things, there are many different ways to handle it, however following a guide or tutorial such as http://code.tutsplus.com/tutorials/android-sdk-create-a-drawing-app-touch-interaction--mobile-19202 is generally the best place to start and pull what you need from it.

Good luck!


This view class will draw grid of equidistance lines in Canvas based on how much GAP_WIDTH_DP is allocated

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

public class PixelGridView extends View {

    private int screenWidth;
    private int screenHeight;
    public static final int GAP_WIDTH_DP = 62;
    private float GAP_WIDTH_PIXEL;
    private  Paint paint = new Paint();

    public PixelGridView(Context context) {
        super(context);
        init(context);
    }

    public PixelGridView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);

    }

    public PixelGridView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public PixelGridView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    public static float convertDpToPixel(float dp, Context context){
        return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
    }

    public void init(Context context) {

        // set paint color
        paint.setColor(Color.GREEN);

        // get view dimentions
        getScreenDimensions();

    }

    private void getScreenDimensions() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        screenWidth = displayMetrics.widthPixels;
        screenHeight = displayMetrics.heightPixels;

        //gap size in pixel
        GAP_WIDTH_PIXEL = convertDpToPixel(GAP_WIDTH_DP,  getContext());

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        getScreenDimensions();

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // draw Horizontal line from Y= 0 -> Y+=Gap... till screen width
        float verticalPosition = 0;

        while (verticalPosition <= screenHeight) {

            canvas.drawLine(0, verticalPosition,
                           screenWidth, verticalPosition, paint);

            verticalPosition += GAP_WIDTH_PIXEL;

        }

        // draw Vertical line from X=0 -> X+=Gap... till screen Height 0|||hor+gap|||W
        float horizontalPosition = 0;

        while (horizontalPosition <= screenWidth) {

            canvas.drawLine(horizontalPosition, 0,
                    horizontalPosition, screenHeight,paint);

            horizontalPosition += GAP_WIDTH_PIXEL;

        }

    }
}

Need Your Help

git pull analogous to / opposite of git push -f?

git git-push git-pull git-fetch

Sometimes, esp. when I'm the only one working on a remote repository, I like rewriting the history with git rebase -i and git push origin master -f.