How to move in?

Hi everyone, Im currently working on an app on my own on Android Studio, in which i have some features implemented with Java, one of them is a white Canvas that allows you to write-draw with your fingers on the screen (some kind of chalkboard), my question is, how could i implement this kind of things in Fuse? or how can i Implement any UI built on Fuse into my Android project?

thanks in advance :slight_smile:

Hi! I suspect that you’d need to dive into Uno for your finger-painting use case. The input part itself should be fairly easy, you can find the API docs [here](https://www.fusetools.com/learn/uno/api/fuse/input has an overview of the APIs).

However, the rendering bit could be a bit trickier. I guess there’s two possible paths here:

  1. Use hardware accelerated rendering (just as all the custom UI components). Not sure if you’d have to go as far down as writing your own draw statements or if there’s any higher-level functionality that can come to your help.

  2. Create a native canvas and draw directly to that from your Uno code. This is obviously not cross-platform (you’d need custom canvas-setup for both iOS and Android) but the drawing itself would be easier (pretty much the same process as you use today). I’m not 100% sure there’s a way of creating & accessing native canvas elements in Fuse yet though. Let’s see if someone else knows. :slight_smile:

As for putting a Fuse-built UI on top of an existing Android project that’s a feature which isn’t quite ready yet. If you wanted to do it right now I think the only option is to separate out the app logic & internal functionality in a lib and link that into a Fuse project using UXL (warning: UXL is not the easiest thing to master, which is why we’re working on better ways of handling stuff like this)

Hi!

This is totally doable. I guess your Canvas inherits from android.view.View or one of its subclasses? We are working on a feature so that you can drag drop you *.java files into a fuse projects and access it from Uno code. Then android.view.View subclasses can be hosted within the fuse UI tree.

As this feature is not released yet I think we need to help you integrate it in fuse if you need it today :slight_smile:

Yes Vegard, Indeed i work with a custom View, and it is a fragment and i do not make refence to the default Layout (XML) here i put the code Im using (a little messy maybe)

import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.EmbossMaskFilter; import android.graphics.MaskFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout;

import java.io.File; import java.io.FileOutputStream;

public class PintarActivity extends Fragment implements MenuDialog.OnColorChangedListener {

protected RelativeLayout lLayout;

protected FragmentActivity faActivity;
MyView mv;
AlertDialog dialog;



public View onCreateView(LayoutInflater inflater, ViewGroup container , Bundle savedInstanceState) {


   faActivity  = (FragmentActivity) super.getActivity();
   lLayout    = (RelativeLayout) inflater.inflate(R.layout.activity_pintar, container, false);

    mv= new MyView(this);
    mv.setDrawingCacheEnabled(true);
    //mv.setBackgroundResource(R.drawable.afor);//poner un background
    mv.setBackgroundColor(0xFFFFFFFF);

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(0xFF000000);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(10);
    mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },
            0.4f, 6, 3.5f);
    mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
return mv;
}
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}
private Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter  mBlur;

public void colorChanged(int color) {
    mPaint.setColor(color);
}

public class MyView extends View {

    private static final float MINP = 0.25f;
    private static final float MAXP = 0.75f;
    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Path mPath;
    private Paint   mBitmapPaint;
    Context context;

    public MyView(Fragment c) {
        super(c.getActivity());
        context=c.getActivity();
        mPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);

    }

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


        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

        canvas.drawPath(mPath, mPaint);
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private void touch_start(float x, float y) {

//showDialog(); mPath.reset(); mPath.moveTo(x, y); mX = x; mY = y;

    }
    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
            mX = x;
            mY = y;
        }
    }
    private void touch_up() {
        mPath.lineTo(mX, mY);

// commit the path to our offscreen mCanvas.drawPath(mPath, mPaint); // con este codigo no se ve el camino que vamos siguiendo / mPath.reset(); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); mPaint.setMaskFilter(null);/ }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:

                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }


}

private static final int COLOR_MENU_ID = Menu.FIRST;
//private static final int EMBOSS_MENU_ID = Menu.FIRST + 1;
//private static final int BLUR_MENU_ID = Menu.FIRST + 2;
private static final int ERASE_MENU_ID = Menu.FIRST + 3;
// private static final int SRCATOP_MENU_ID = Menu.FIRST + 4;
//private static final int Save = Menu.FIRST + 5;
private static final int VOLVER_NEGRO_ID =Menu.FIRST+1 ;

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu,inflater);

    menu.add(0, COLOR_MENU_ID, 0, "Color").setShortcut('3', 'c');
   // menu.add(0, EMBOSS_MENU_ID, 0, "relieve").setShortcut('4', 's');
    //menu.add(0, BLUR_MENU_ID, 0, "Blur").setShortcut('5', 'z');
    menu.add(0, ERASE_MENU_ID, 0, "Borrar").setShortcut('5', 'z');
   // menu.add(0, SRCATOP_MENU_ID, 0, "SrcATop").setShortcut('5', 'z');
    menu.add(0, VOLVER_NEGRO_ID ,0,"Color negro").setShortcut('5','z');
    //menu.add(0, Save, 0, "Save").setShortcut('5', 'z');

}

@Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); //return true; }

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    mPaint.setXfermode(null);
    mPaint.setAlpha(0xFF);

    switch (item.getItemId()) {
        case COLOR_MENU_ID:
            new MenuDialog(this.getActivity(),this,mPaint.getColor()).show();
            mPaint.setStrokeWidth(10);
            return true;
        /*case EMBOSS_MENU_ID:
            if (mPaint.getMaskFilter() != mEmboss) {
                mPaint.setMaskFilter(mEmboss);
            } else {
                mPaint.setMaskFilter(null);
            }
            return true;*/
        /*case BLUR_MENU_ID:
            if (mPaint.getMaskFilter() != mBlur) {
                mPaint.setMaskFilter(mBlur);
            } else {
                mPaint.setMaskFilter(null);
            }
            return true;*/
        case ERASE_MENU_ID:
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            mPaint.setAlpha(0x80);
            mPaint.setStrokeWidth(40);
            return true;
      /*  case SRCATOP_MENU_ID:

            mPaint.setXfermode(new PorterDuffXfermode(
                    PorterDuff.Mode.SRC_ATOP));
            mPaint.setAlpha(0x80);
            return true;*/
        case VOLVER_NEGRO_ID:
            mPaint.setStrokeWidth(10);
            mPaint.setColor(0xFF000000);
            return true;
       /* case Save:
            AlertDialog.Builder editalert = new AlertDialog.Builder(PintarActivity.this.getActivity());
            editalert.setTitle("Ingresa el nombre con el cual deseas Guardar");
            final EditText input = new EditText(PintarActivity.this.getActivity());
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.FILL_PARENT,
                    LinearLayout.LayoutParams.FILL_PARENT);
            input.setLayoutParams(lp);
            editalert.setView(input);
            editalert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {

                    String name= input.getText().toString();
                    Bitmap bitmap = mv.getDrawingCache();

                    String path = Environment.getExternalStorageDirectory().getAbsolutePath();
                    File file = new File("/sdcard/"+name+".png");
                    try
                    {
                        if(!file.exists())
                        {
                            file.createNewFile();
                        }
                        FileOutputStream ostream = new FileOutputStream(file);
                        bitmap.compress(Bitmap.CompressFormat.PNG, 10, ostream);
                        ostream.close();
                        mv.invalidate();
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }finally
                    {

                        mv.setDrawingCacheEnabled(false);
                    }
                }
            });

            editalert.show();
            return true;*/
    }
    return super.onOptionsItemSelected(item);
}

}

Hope you guys can guide me on how to move from that Java code to Uno because im still reading at the documentation on the link that remy kindly pasted in a comment above :slight_smile: